kick
|
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 }