kick
/Users/morten/Programmering/cpp/kick/src/kick/2d/canvas.cpp
00001 //
00002 // Created by morten on 26/07/14.
00003 //
00004 
00005 #include "canvas.h"
00006 #include "label.h"
00007 #include "kick/2d/sprite.h"
00008 #include "kick/2d/button.h"
00009 #include "kick/2d/toggle_button.h"
00010 #include "kick/scene/scene.h"
00011 #include "kick/2d/component2d.h"
00012 #include "kick/material/material.h"
00013 #include "kick/mesh/mesh.h"
00014 #include "kick/mesh/mesh_data.h"
00015 #include "glm/glm.hpp"
00016 #include "kick/core/engine.h"
00017 #include <algorithm>
00018 #include <iostream>
00019 
00020 using namespace kick;
00021 using namespace std;
00022 using namespace glm;
00023 
00024 namespace kick{
00025 
00026     Canvas::Canvas(GameObject *gameObject) : ComponentRenderable(gameObject) {
00027         mMesh = new Mesh();
00028         mMeshData = make_shared<MeshData>();
00029         mMeshData->setMeshUsage(MeshUsage::DynamicDraw);
00030         mMesh->setMeshData(mMeshData);
00031         mMaterial = new Material();
00032         for (auto c : gameObject->componentsInChildren<Component2D>()){
00033             registerComponent2D(c);
00034         }
00035     }
00036 
00037     Canvas::~Canvas() {
00038 
00039     }
00040 
00041     void Canvas::render(kick::EngineUniforms *engineUniforms, Material* replacementMaterial) {
00042         if (!enabled()){
00043             return;
00044         }
00045         sort(mComponents.begin(), mComponents.end(), [](std::shared_ptr<Component2D> c1, std::shared_ptr<Component2D> c2){
00046             if (c1->order() != c2->order()){
00047                 return c1->order() < c2->order();
00048             }
00049             return c1->shader() < c2->shader();
00050         });
00051 
00052         vector<Sprite*> sprites;
00053         TextureAtlas*currentTextureAtlas = nullptr;
00054         for (auto& comp : mComponents){
00055             auto text = dynamic_pointer_cast<Label>(comp);
00056             if (text){
00057                 renderSprites(sprites, engineUniforms, replacementMaterial); // render previous sprites
00058                 text->render(engineUniforms);
00059             } else {
00060                 auto sprite = dynamic_pointer_cast<Sprite>(comp);
00061                 if (currentTextureAtlas != sprite->textureAtlas().get()){
00062                     renderSprites(sprites, engineUniforms, replacementMaterial);
00063                     currentTextureAtlas = sprite->textureAtlas().get();
00064                 }
00065                 if (sprite){
00066                     sprites.push_back(sprite.get());
00067                 }
00068             }
00069         }
00070         renderSprites(sprites, engineUniforms, replacementMaterial);
00071     }
00072 
00073     void Canvas::updateVertexBuffer(std::vector<Sprite*> &sprites) {
00074         vector<vec3> position;
00075         vector<vec2> textureCoords;
00076         vector<vec4> colors;
00077         vector<GLushort> indices;
00078         sort(sprites.begin(), sprites.end(), [](Sprite* s1, Sprite* s2){
00079             return s1->order() < s2->order();
00080         });
00081         unsigned short index = 0;
00082         for (unsigned short i=0;i<sprites.size();i++){
00083             auto sprite = sprites[i];
00084             auto transform = sprite->transform();
00085 
00086             vec2 size = (vec2) sprite->textureAtlas()->textureSize();
00087             mat4 toWorld = transform->globalMatrix();
00088             vec2 scale = sprite->scale();
00089 
00090 
00091             TextureAtlasEntry entry = sprite->entry();
00092             Bounds2 bounds = sprite->trimmedBounds();
00093             vec4 color = sprite->color();
00094 
00095             if (sprite->type() == SpriteType::Simple) {
00096                 position.push_back((vec3) (toWorld * vec4{bounds.lowLeft(), 0, 1}));
00097                 position.push_back((vec3) (toWorld * vec4{bounds.lowRight(), 0, 1}));
00098                 position.push_back((vec3) (toWorld * vec4{bounds.upperRight(), 0, 1}));
00099                 position.push_back((vec3) (toWorld * vec4{bounds.upperLeft(), 0, 1}));
00100 
00101                 vec2 min{entry.frame.x / size.x, 1.0 - (entry.frame.y + entry.frame.w) / size.y};
00102                 vec2 max{(entry.frame.x + entry.frame.z) / size.x, 1.0 - entry.frame.y / size.y};
00103                 textureCoords.push_back(vec2{min.x, min.y});
00104                 textureCoords.push_back(vec2{max.x, min.y});
00105                 textureCoords.push_back(vec2{max.x, max.y});
00106                 textureCoords.push_back(vec2{min.x, max.y});
00107 
00108                 colors.push_back(color);
00109                 colors.push_back(color);
00110                 colors.push_back(color);
00111                 colors.push_back(color);
00112 
00113                 // push two triangles
00114                 indices.push_back(index);
00115                 indices.push_back(index + 1);
00116                 indices.push_back(index + 2);
00117                 indices.push_back(index + 2);
00118                 indices.push_back(index + 3);
00119                 indices.push_back(index);
00120                 index+=4;
00121             }
00122             else if (sprite->type() == SpriteType::Sliced) {
00123                 vec2 dim {entry.frame.z,entry.frame.w};
00124                 vec4 sliceX = vec4{bounds.min.x, bounds.min.x + dim.x * sprite->sliceX()[0],bounds.max.x - dim.x * (1.0- sprite->sliceX()[1]), bounds.max.x};
00125                 vec4 sliceY = vec4{bounds.min.y, bounds.min.y + dim.y * sprite->sliceY()[0],bounds.max.y - dim.y * (1.0- sprite->sliceY()[1]), bounds.max.y};
00126 
00127                 vec2 min{entry.frame.x / size.x, 1.0 - (entry.frame.y + entry.frame.w) / size.y};
00128                 vec2 max{(entry.frame.x + entry.frame.z) / size.x, 1.0 - entry.frame.y / size.y};
00129                 vec4 uvX{min.x, lerp(min.x, max.x, sprite->sliceX()[0]),lerp(min.x, max.x, sprite->sliceX()[1]),max.x};
00130                 vec4 uvY{min.y, lerp(min.y, max.y, sprite->sliceY()[0]),lerp(min.y, max.y, sprite->sliceY()[1]),max.y};
00131 
00132                 for (int x=0;x<3;x++){
00133                     for (int y=0;y<3;y++){
00134                         position.push_back((vec3) (toWorld * vec4{sliceX[x], sliceY[y], 0, 1}));
00135                         position.push_back((vec3) (toWorld * vec4{sliceX[x+1], sliceY[y], 0, 1}));
00136                         position.push_back((vec3) (toWorld * vec4{sliceX[x+1], sliceY[y+1], 0, 1}));
00137                         position.push_back((vec3) (toWorld * vec4{sliceX[x], sliceY[y+1], 0, 1}));
00138 
00139                         textureCoords.push_back(vec2{uvX[x], uvY[y]});
00140                         textureCoords.push_back(vec2{uvX[x+1], uvY[y]});
00141                         textureCoords.push_back(vec2{uvX[x+1], uvY[y+1]});
00142                         textureCoords.push_back(vec2{uvX[x], uvY[y+1]});
00143 
00144                         colors.push_back(color);
00145                         colors.push_back(color);
00146                         colors.push_back(color);
00147                         colors.push_back(color);
00148 
00149                         // push two triangles
00150                         indices.push_back(index);
00151                         indices.push_back(index + 1);
00152                         indices.push_back(index + 2);
00153                         indices.push_back(index + 2);
00154                         indices.push_back(index + 3);
00155                         indices.push_back(index);
00156                         index+=4;
00157                     }
00158                 }
00159             }
00160         }
00161         mMeshData->setPosition(position);
00162         mMeshData->setColor(colors);
00163         mMeshData->setTexCoord0(textureCoords);
00164         mMeshData->setSubmesh(0,indices, MeshType::Triangles);
00165         mMesh->setMeshData(mMeshData);
00166     }
00167 
00168     void Canvas::renderSprites(vector<Sprite*> &sprites, kick::EngineUniforms *engineUniforms, Material* replacementMaterial) {
00169         if (sprites.size() == 0){
00170             return;
00171         }
00172         updateVertexBuffer(sprites);
00173 
00174         auto mat = replacementMaterial ? replacementMaterial : mMaterial;
00175         if (!replacementMaterial ){
00176             mMaterial->setShader(sprites[0]->textureAtlas()->shader());
00177             mMaterial->setUniform("mainTexture", sprites[0]->textureAtlas()->texture());
00178         }
00179         auto shader = mat->shader();
00180         assert(shader);
00181         mMesh->bind(shader.get());
00182 
00183         shader->bind_uniforms(mMaterial, engineUniforms, transform().get());
00184 
00185         mMesh->render(0);
00186 
00187         sprites.clear();
00188     }
00189 
00190     int Canvas::renderOrder() {
00191         return 0;
00192     }
00193 
00194     std::shared_ptr<Camera> Canvas::camera() const {
00195         return mCamera;
00196     }
00197 
00198     void Canvas::setCamera(std::shared_ptr<Camera> camera) {
00199         Canvas::mCamera = camera;
00200         mGameObject->setLayer(256);
00201     }
00202 
00203     void Canvas::deactivated() {
00204         for (auto c : mComponents){
00205             deregisterComponent2D(c);
00206         }
00207     }
00208 
00209     void Canvas::registerComponent2D(std::shared_ptr<Component2D> comp) {
00210         mComponents.push_back(comp);
00211         comp->mCanvas = dynamic_pointer_cast<Canvas>(shared_from_this());
00212 
00213 
00214         auto sml = dynamic_pointer_cast<SpriteMouseListener>(comp);
00215         if (sml){
00216             mMouseListeners.push_back(sml);
00217         }
00218     }
00219 
00220     void Canvas::deregisterComponent2D(std::shared_ptr<Component2D> comp) {
00221         auto pos = find(mComponents.begin(), mComponents.end(), comp);
00222         if (pos != mComponents.end()){
00223             (*pos)->mCanvas = nullptr;
00224             mComponents.erase(pos);
00225 
00226             auto sml = dynamic_pointer_cast<SpriteMouseListener>(comp);
00227             auto pos2 = find(mMouseListeners.begin(), mMouseListeners.end(), sml);
00228             if (pos2 != mMouseListeners.end()){
00229                 mMouseListeners.erase(pos2);
00230             }
00231         }
00232     }
00233 
00234     std::shared_ptr<Sprite> Canvas::createSprite(std::shared_ptr<TextureAtlas> textureAtlas, std::string spriteName, glm::vec2 pos) {
00235         auto sprite = addComponent<Sprite>();
00236         sprite->setTextureAtlas(textureAtlas);
00237         sprite->setSpriteName(spriteName);
00238         return sprite;
00239     }
00240 
00241     std::shared_ptr<Button> Canvas::createButton(std::string text) {
00242         std::shared_ptr<TextureAtlas> textureAtlas = Project::loadTextureAtlas("assets/ui/ui.txt");
00243         auto button = addComponent<Button>();
00244 
00245         button->setTextureAtlas(textureAtlas);
00246         button->setNormalSprite("button-normal.png");
00247         button->setHoverSprite("button-hover.png");
00248         button->setPressedSprite("button-pressed.png");
00249         button->setScale({2,2});
00250         button->setText(text);
00251 
00252         return button;
00253     }
00254 
00255     std::shared_ptr<Label> Canvas::createLabel(std::string text, int fontsize) {
00256         auto labelComponent = addComponent<Label>();
00257 
00258 
00259         auto font = Project::loadFont(fontsize);
00260         labelComponent->setFont(font);
00261         labelComponent->setText(text);
00262 
00263         return labelComponent;
00264     }
00265 
00266     void Canvas::updateRenderOrder(std::shared_ptr<Component2D> comp) {
00267         // todo
00268     }
00269 
00270 
00271     void Canvas::update() {
00272         if (mCamera ==nullptr || !enabled()) {
00273             return;
00274         }
00275         vec2 mousePosition = (vec2) MouseInput::position();
00276 
00277         vec2 screensize = (vec2) Engine::context()->getContextSurfaceDim();
00278         vec2 mouseClipCoord = ((mousePosition / screensize)*2.0f-vec2{1.0})*vec2{1,-1}; // correct
00279 
00280         mat4 viewProjection = inverse(mCamera->projectionMatrix() * mCamera->viewMatrix());
00281         vec2 mouseWorldCoord = (vec2)(viewProjection * vec4(mouseClipCoord, 0, 1));
00282 
00283         for (auto ml : mMouseListeners){
00284             auto sprite = dynamic_pointer_cast<Sprite>(ml);
00285             Bounds2 bounds = sprite->trimmedBounds();
00286             vec2 mouseLocalCoord = (vec2)(sprite->transform()->globalTRSInverse() * vec4{mouseWorldCoord, 0, 1});
00287 
00288             auto mouseOverIter = find(mMouseOver.begin(), mMouseOver.end(), ml);
00289             bool wasMouseOver = mouseOverIter != mMouseOver.end();
00290             if (bounds.contains(mouseLocalCoord)){
00291                 if (!wasMouseOver){
00292                     ml->over();
00293                     mMouseOver.push_back(ml);
00294                 }
00295                 for (int i=0;i<3;i++){
00296                     if (MouseInput::down(i)){
00297                         ml->down(i);
00298                         mMousePressed.push_back({ml, i});
00299                     }
00300                 }
00301 
00302             } else {
00303                 if (wasMouseOver){
00304                     ml->out();
00305                     mMouseOver.erase(mouseOverIter);
00306                 }
00307             }
00308         }
00309         for (int i = mMousePressed.size()-1;i>=0;i--){
00310             int button = mMousePressed[i].second;
00311             if (MouseInput::pressed(button)){
00312                 mMousePressed[i].first->pressed(button);
00313             } else {
00314                 mMousePressed[i].first->up(button);
00315                 mMousePressed.erase(mMousePressed.begin()+i);
00316             }
00317         }
00318     }
00319 
00320     std::shared_ptr<ToggleButton> Canvas::createToggleButton(std::string text) {
00321         std::shared_ptr<TextureAtlas> textureAtlas = Project::loadTextureAtlas("assets/ui/ui.txt");
00322 
00323         auto button = addComponent<ToggleButton>();
00324         button->setTextureAtlas(textureAtlas);
00325         button->setNormalSprite("button-normal.png");
00326         button->setHoverSprite("button-hover.png");
00327         button->setPressedSprite("button-pressed.png");
00328         button->setSelectedSprite("button-pressed.png");
00329         button->setScale({2,2});
00330         button->setText(text);
00331 
00332         return button;
00333     }
00334 
00335     GameObject *Canvas::createGameObject() {
00336         return gameObject()->scene()->createGameObject();
00337     }
00338 }
 All Classes Functions Variables