2D Diamond (isometric) map editor - Are textures expanding endlessly?

I am currently developing a 2D isometric map editor. I show an object (cube, player) that contains dots and textures. Each cube consists of 12 points (12 points, but is processed as 3 sides of 4 points when displaying sfml (sf :: VertexArray).

(I know that I include some ".cpp" times at times, I have a problem with my IDE (visual studio), which I am trying to solve, please do not worry.)

main.cpp

#pragma once #include "globalfunctions.h" //global functions + main headers + class headers int main() { int mapSize = 0; int cubeSize = 0; cout << "Map size: "; cin >> mapSize; cout << endl; cout << "Cube size: "; cin >> cubeSize; cout << endl; int windowWidth = (mapSize * cubeSize) - (cubeSize * 2); int windowHeight = ((mapSize * cubeSize) - (cubeSize * 2)) / 2; renderWindow window(windowWidth, windowHeight, mapSize, cubeSize); int nbMaxTextures = 9; for (int t = 0; t < nbMaxTextures; t++) { window.loadTexture("test", t); } window.run(); return EXIT_SUCCESS; } 

globalfunctions.h

 #pragma once #include <SFML/System.hpp> #include <SFML/Graphics.hpp> #include <SFML/Window.hpp> #include <iostream> #include <math.h> //#include <sstream> #include <vector> using namespace std; sf::Vector2u isometricToCartesian(int i, int j, int cubeSize) { sf::Vector2u carth; carth.x = (j - i) * (cubeSize / 2); carth.y = (j + i) * (cubeSize / 4); return carth; } sf::Vector2u cartesianToIsometric(int x, int y, int cubeSize) {//TODO sf::Vector2u iso; iso.x = 0; iso.y = 0; return iso; } #include "entity.h" #include "renderWindow.h" 

renderWindow.h

 #pragma once class renderWindow { public: renderWindow(float WIDTH, float HEIGHT, int MAPSIZE, int CUBESIZE); void run(); void loadTexture(sf::String folder, int numTexture); //SETTERS //... //GETTERS //... private: int mCurrentLayerID; int mMapSize; int mCubeSize; int mSelectedTexture; vector<entity> mMap; sf::RenderWindow mWindow; vector<sf::Texture> mTextures; sf::Texture mMemoryTexture; void processEvent(); void update(sf::Time deltaTime); void render(); //CUBE ACTION------------------------------------------- void addCube(int layerID, float x, float y); entity& getCube(int ID); entity& getCubeAt(float x, float y); vector<sf::VertexArray> loadCube(int cubeID);//UPDATE DATA LIKE COORDINATES -> create/chnge the vertex void drawCube(int cubeID);//draw the vertex //VARIABLES vector<sf::VertexArray> verticesSide1; vector<sf::VertexArray> verticesSide2; vector<sf::VertexArray> verticesSide3; //CUBE ACTION------------------------------------------- }; #include "renderWindow.cpp" 

renderWindow.cpp

 #pragma once renderWindow::renderWindow(float WIDTH, float HEIGHT, int MAPSIZE, int CUBESIZE) : mWindow(sf::VideoMode(WIDTH, HEIGHT), "") { mMapSize = MAPSIZE; mCubeSize = CUBESIZE; mSelectedTexture = 6; mCurrentLayerID = -1; int x = 0; int y = 0; //default layer for (int j = 0; j < mMapSize; j++) { for (int i = 0; i < mMapSize; i++) { x = isometricToCartesian(i, j, mCubeSize).x; y = isometricToCartesian(i, j, mCubeSize).y; addCube(0, x, y); } } for (int c = 0; c < mMap.size(); c++) { verticesSide1.push_back(loadCube(c)[0]); verticesSide2.push_back(loadCube(c)[1]); verticesSide3.push_back(loadCube(c)[2]); //then only do that when something the cube coordinate changed } } void renderWindow::run() { sf::Clock clock; sf::Time timeSinceLastUpdate = sf::Time::Zero; sf::Time TimePerFrame = sf::seconds(1.f / 60.f); while (mWindow.isOpen()) { processEvent(); timeSinceLastUpdate += clock.restart(); while (timeSinceLastUpdate > TimePerFrame) { timeSinceLastUpdate -= TimePerFrame; processEvent(); update(TimePerFrame); } render(); } } void renderWindow::loadTexture(sf::String folder, int numTexture) { if (mMemoryTexture.loadFromFile("textures/" + folder + "/" + to_string(numTexture) + ".jpg")) mTextures.push_back(mMemoryTexture); else cout << "Texture n°" << numTexture << " as failed to load." << endl; } //SETTERS //... //GETTERS //... //PRIVATE METHODE void renderWindow::processEvent() { sf::Event event; while (mWindow.pollEvent(event)) { switch (event.type) { case sf::Event::Closed: mWindow.close(); break; case sf::Event::KeyPressed: if (event.key.code == sf::Keyboard::Escape) mWindow.close(); break; case sf::Event::MouseButtonPressed: if (event.MouseButtonPressed == sf::Mouse::Left) getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(0, mSelectedTexture);//TEST getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(1, mSelectedTexture + 1);//TEST getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(2, mSelectedTexture + 2);//TEST break; /*case sf::Event::MouseMoved: cout << "(" << event.mouseMove.x << ", " << event.mouseMove.y << ")" << endl; break;*/ } } } void renderWindow::update(sf::Time deltaTime) { //REMEMBER: distance = speed * time //MOVEMENT, ANIMATIONS ETC. ... } void renderWindow::render() { mWindow.clear(); for (int c = 0; c < mMap.size(); c++) { drawCube(c); } mWindow.display(); } //CUBE ACTION------------------------------------------- void renderWindow::addCube(int layerID, float x, float y) { //Thoses make the code more readable: int half_cubeSize = mCubeSize / 2; int oneQuarter_cubeSize = mCubeSize / 4; int twoQuarter_cubeSize = oneQuarter_cubeSize * 2; int treeQuarter_cubeSize = oneQuarter_cubeSize * 3; mCurrentLayerID = layerID; entity dummy(mMap.size(), 0, layerID); dummy.addPoint(12); dummy.addTexture(6); dummy.addTexture(7); dummy.addTexture(8); //SIDE 1------------------------------------------------ dummy.setPoint(0, x, y + oneQuarter_cubeSize); dummy.setPoint(1, x + half_cubeSize, y + twoQuarter_cubeSize); dummy.setPoint(2, x + half_cubeSize, y + mCubeSize); dummy.setPoint(3, x, y + treeQuarter_cubeSize); //SIDE 2------------------------------------------------ dummy.setPoint(4, x + half_cubeSize, y + twoQuarter_cubeSize); dummy.setPoint(5, x + mCubeSize, y + oneQuarter_cubeSize); dummy.setPoint(6, x + mCubeSize, y + treeQuarter_cubeSize); dummy.setPoint(7, x + half_cubeSize, y + mCubeSize); //SIDE 3------------------------------------------------ dummy.setPoint(8, x, y + oneQuarter_cubeSize); dummy.setPoint(9, x + half_cubeSize, y); dummy.setPoint(10, x + mCubeSize, y + oneQuarter_cubeSize); dummy.setPoint(11, x + half_cubeSize, y + twoQuarter_cubeSize); mMap.push_back(dummy); } entity& renderWindow::getCube(int ID) { for (int c = 0; c < mMap.size(); c++) { if (mMap[c].getID() == ID) return mMap[c]; } } entity& renderWindow::getCubeAt(float x, float y) {//TO DO return entity(-1, 0, 0); } vector<sf::VertexArray> renderWindow::loadCube(int cubeID) { vector<sf::VertexArray> vertices; vertices.push_back(sf::VertexArray()); vertices.push_back(sf::VertexArray()); vertices.push_back(sf::VertexArray()); vertices[0].setPrimitiveType(sf::Quads); vertices[0].resize(4); vertices[1].setPrimitiveType(sf::Quads); vertices[1].resize(4); vertices[2].setPrimitiveType(sf::Quads); vertices[2].resize(4); sf::Vector2f tv0 = sf::Vector2f(0, 0); sf::Vector2f tv1 = sf::Vector2f(mCubeSize, 0); sf::Vector2f tv2 = sf::Vector2f(mCubeSize, mCubeSize); sf::Vector2f tv3 = sf::Vector2f(0, mCubeSize); sf::Vector2f v0 = sf::Vector2f(getCube(cubeID).getPoint(0, 0), getCube(cubeID).getPoint(0, 1)); sf::Vector2f v1 = sf::Vector2f(getCube(cubeID).getPoint(1, 0), getCube(cubeID).getPoint(1, 1)); sf::Vector2f v2 = sf::Vector2f(getCube(cubeID).getPoint(2, 0), getCube(cubeID).getPoint(2, 1)); sf::Vector2f v3 = sf::Vector2f(getCube(cubeID).getPoint(3, 0), getCube(cubeID).getPoint(3, 1)); sf::Vector2f v4 = sf::Vector2f(getCube(cubeID).getPoint(4, 0), getCube(cubeID).getPoint(4, 1)); sf::Vector2f v5 = sf::Vector2f(getCube(cubeID).getPoint(5, 0), getCube(cubeID).getPoint(5, 1)); sf::Vector2f v6 = sf::Vector2f(getCube(cubeID).getPoint(6, 0), getCube(cubeID).getPoint(6, 1)); sf::Vector2f v7 = sf::Vector2f(getCube(cubeID).getPoint(7, 0), getCube(cubeID).getPoint(7, 1)); sf::Vector2f v8 = sf::Vector2f(getCube(cubeID).getPoint(8, 0), getCube(cubeID).getPoint(8, 1)); sf::Vector2f v9 = sf::Vector2f(getCube(cubeID).getPoint(9, 0), getCube(cubeID).getPoint(9, 1)); sf::Vector2f v10 = sf::Vector2f(getCube(cubeID).getPoint(10, 0), getCube(cubeID).getPoint(10, 1)); sf::Vector2f v11 = sf::Vector2f(getCube(cubeID).getPoint(11, 0), getCube(cubeID).getPoint(11, 1)); vertices[0][0] = sf::Vertex(v0, tv0); vertices[0][1] = sf::Vertex(v1, tv1); vertices[0][2] = sf::Vertex(v2, tv2); vertices[0][3] = sf::Vertex(v3, tv3); vertices[1][0] = sf::Vertex(v4, tv0); vertices[1][1] = sf::Vertex(v5, tv1); vertices[1][2] = sf::Vertex(v6, tv2); vertices[1][3] = sf::Vertex(v7, tv3); vertices[2][0] = sf::Vertex(v8, tv0); vertices[2][1] = sf::Vertex(v9, tv1); vertices[2][2] = sf::Vertex(v10, tv2); vertices[2][3] = sf::Vertex(v11, tv3); return vertices; } void renderWindow::drawCube(int cubeID) { mWindow.draw(verticesSide1[cubeID], &mTextures[getCube(cubeID).getTexture(0)]); mWindow.draw(verticesSide2[cubeID], &mTextures[getCube(cubeID).getTexture(1)]); mWindow.draw(verticesSide3[cubeID], &mTextures[getCube(cubeID).getTexture(2)]); } //CUBE ACTION------------------------------------------- 

entity.h

  #pragma once class entity { public: entity(); entity(int id, int type, int numlayer); void addPoint(int nbPoints); void addTexture(int numTexture); //SETTERS void setPoint(int numPoint, float x, float y); void setTexture(int textureID, int numTexture); //GETTERS int getID(); float getPoint(int numPoint, int numIndex);//if numIndex = 0 -> x || if numIndex = 1 -> y int getType(); int getNumLayer(); int getTexture(int numTexture); private: int mID; int mType; int mNumLayer; vector<sf::Vector2u> mPoints; vector<int> mTextures; }; #include "entity.cpp" 

entity.cpp

 #pragma once entity::entity() { mID = 0; mType = -1; mNumLayer = 0; } entity::entity(int id, int type, int numlayer) { mID = id; mType = type; mNumLayer = numlayer; } void entity::addPoint(int nbPoints) { mPoints.clear(); int newSize = 0; for (int p = 0; p < nbPoints; p++) { newSize++; } mPoints = vector<sf::Vector2u>(newSize); } void entity::addTexture(int numTexture) { mTextures.push_back(numTexture); } //SETTERS void entity::setPoint(int numPoint, float x, float y) { mPoints[numPoint].x = x; mPoints[numPoint].y = y; } void entity::setTexture(int textureID, int numTexture) { mTextures[textureID] = numTexture; } //GETTERS int entity::getID() { return mID; } float entity::getPoint(int numPoint, int numIndex) { if (numIndex == 0) return mPoints[numPoint].x; else return mPoints[numPoint].y; } int entity::getType() { return mType; } int entity::getNumLayer() { return mNumLayer; } int entity::getTexture(int numTexture) { return mTextures[numTexture]; } 

I did a lot of tests, too many, so I won’t post them right now, but if you have any questions, feel free to ask.

Here is the problem described in the title:

enter image description here

And here are screens with an image of only one face (in the same order in the code):

Side 1 is only displayed

Side 2 is only displayed

Side 3 is displayed only

The only thing I don’t understand is that the cube displayed separately works fine if you enter the coordinates manually. Even extended ones. But the coordinate formula is good ... (I noticed that the cube n ° 50 for a 15x15 map with a 64x64 cube displays a rectangle "infinite" in width) If the texture expands (possibly to infinity), this means that the coordinates are constantly growing somewhere ? Then why are the cubes still well placed?

Here are the assets (64 * 64 png): enter image description here enter image description here enter image description here Directories: textures / test /

+5
source share
3 answers

Not quite the answer (since the code will be rewritten anyway), there are so few hints of the new code (some of them are already mentioned in the comments).

  • Tileset

    In the final isometric engine, use sprites. They are faster and support pixel art. For my purposes, I use a compilation of these two free element sets ( 64x64 ):

    Both are compatible. I compiled and edited them to suit the needs of my engine. So this is what I use (still working):

    my tileset

    White color 0x00FFFFFF means transparency. To the right is not enough. I added information about tile height and bends.

    If you see the first 4 tiles in the upper left corner, they are all the same, rotated 90 degrees. Thus, all my tiles have an index of 4 tiles (rotation of 90 degrees) int rot[4] . That way I can rotate the whole map or just browse. I will compile the set so that the rotation is next to each other. There are 3 options:

    • tile[ix].rot[]={ ix,ix,ix,ix }; where ix is a tile without rotation (earth)
    • tile[ix].rot[]={ ix,ix+1,ix,ix+1 }; where ix is a tile with 2 turns (these 2 tiles with a piece of chopped wood in the middle right)
    • tile[ix].rot[]={ ix,ix+1,ix+2,ix+3 }; where ix is a tile with 4 turns (for example, the first tile)

    Indexes are valid for rough only for the first tile, the rest have a whole array rot[] , rotated 1 value from the neighbor. Some rotations are invisible (see Wide Trees), but tile is still present for rotation.

    Tile height is important for placing tiles when editing, as well as for creating automatic maps.

    cache information

    I plan to add an A* map for each fragment as well, so that I can use path searches or calculate watter streams and much more.

  • Map editor

    I prefer 3D maps. With higher resolution, you need to choose the right viewing area to view in order to maximize performance. It is also a good idea to create an empty underground so that the rendering is much faster (this can be done almost during the rendering process without the need to update the map).

    I recommend encoding these functions:

    • make earth void
    • make the earth solid.
    • random terrain (diamond and square)
    • filter out small holes and smooth edges (add slope tiles to cubic)
  • fragment editor

    In addition to the obvious paint editor, you should add a few more features such as:

    • floor <-> ceiling
    • left <-> right
    • front <-> back
    • divide the large sprite into regular tiles.
    • copy / merge / paste
    • adjust lighting after left <-> mirror image operation

    They are very useful when compiling / editing set resources. As you can see, my tile set has many fragments that are not present in the source sets. They were created by these functions + small mask editing ... Colored masks on the bottom of the cache are used to mask and correctly combine parts of the tiles to create the missing ... (you can take one side shape of the tile and others from others ...)

    tile operations

[Note]

For more information / ideas refer to some related Q / A s:

+3
source

In OpenGL, when creating an OpenGL texture manually, you can assign 4 types:

 GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP and GL_CLAMP_TO_BORDER 

If you want to know more about the differences in openGL textures, see here . Basically, it extends the last pixel in the image to the rest of the reserved memory.

To solve your problem, try loading the texture by changing the settings. I do not know if sfml is allowed to do this with the heading Texture.hpp, setRepeated appears in the link , try setting true to see the problem. Another way: loadfromfile with the size sf :: IntRect (0, 0, 32, 32) in the example.

This code has not been tested, but theoretically using OpenGL will work:

 void renderWindow::loadTexture(sf::String folder, int numTexture) { if (mMemoryTexture.loadFromFile("textures/" + folder + "/" + to_string(numTexture) + ".jpg")) mTextures.push_back(mMemoryTexture); else cout << "Texture n°" << numTexture << " as failed to load." << endl; // Generate OpenGL Texture manually GLuint texture_handle; glGenTextures(1, &texture_handle); // Attach the texture glBindTexture(GL_TEXTURE_2D, texture_handle); // Upload to Graphic card glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mMemoryTexture.GetWidth(), mMemoryTexture.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, mMemoryTexture.GetPixelsPtr() ); // Set the values glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } 

Perhaps this will help you solve your problem.

0
source

I'm sure finding a better way to do this code thanks to the stackoverflow members. For people who got there looking for a solution to a similar problem, I suggest you familiarize yourself with the comments for some useful links and comments.

0
source

Source: https://habr.com/ru/post/1245856/


All Articles