2D等距投影 - SFML - 正确的公式,错误的坐标范围

18

我不使用瓷砖,而是用 sf::Vertex 绘制立方体。每个立方体有 6 个面,每个面有 4 个点。

enter image description here

所以我只需要使用 cubes[numCube].sides()[numSide].... 来选择一个面。

我在 layer.cpp 中创建了立方体。

for(int J = 0; J < mapSize; J++)
    {
        for(int I = 0; I < mapSize; I++)
        {
            x = (J - I) * (cubeSize/2);
            y = (J + I) * (cubeSize/4);

            c = new cube(cubeSize, x, y, z, I, J);
            cs.push_back(*c);
        }
    }
在 cube.cpp 中,我创建了方块的面,然后在 sides.cpp 中,我按照以下方式计算每个点的坐标:
switch(typeSide)
{
    case 0://DOWN_SIDE
        light = 1;

        tmp_x = x + (size/2);
        tmp_y = y + (size/2);
        p0 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x + size;
        tmp_y = y + (3 * (size/4));
        p1 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x + (size/2);
        tmp_y = y + size;
        p2 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x;
        tmp_y = y + (3 * (size/4));
        p3 = new point(tmp_x, tmp_y, tmp_z);
        break;

    case 1://BACK_LEFT_SIDE

//ETC. ....

Point.cpp :

/*
 * point.cpp
 *
 *  Created on: 21 nov. 2015
 *      Author: user
 */

#include "point.h"

point::point(float tx, float ty, float tz)
{
    coords* dummyVar = new coords(tx, ty, tz);
    coordinates = dummyVar;
}

std::vector<float> point::position()//Use : myPoint.getPosition[0] //get the x
{
    std::vector<float> dummyVar;

    dummyVar.push_back(coordinates->getX());
    dummyVar.push_back(coordinates->getY() - coordinates->getZ());

    return dummyVar;
}

void point::move(float tx, float ty, float tz)
{
    coordinates->setX(tx);
    coordinates->setY(ty);
    coordinates->setZ(tz);
}

我的问题源于用来检测点击的函数:

if (event.type == sf::Event::MouseMoved)
{
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
}

该函数(不用在意注释):

我尝试获取我的立方体向量中一个立方体的条目,而无需使用'for循环'。 为什么?为了在点击时使用更少的CPU。

int map::getCubeIDAt(float x, float y, int offsetLeft, int offsetTop, bool enableOffset)//WIP ! //USED FOR CLICK DETECTION ON CUBES
    {
    //----------------------------------------------------------------//
        int unsigned entry = -1;

        int I = 0;
        int J = 0;
    //----------------------------------------------------------------//

        if(currentLayerId() > -1)//If there is any layers
        {
            //IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
            //{

                if(!enableOffset)//With offsets disabled
                {
                    I = (y * 2 - x) / cubeSize;
                    J = (y * 2 + x) / cubeSize;
                }
                else //With offsets enabled
                {
                    I = (((y-offsetTop)+(currentLayerId()*(cubeSize/2))) * 2 - (x-offsetLeft)) / cubeSize;
                    J = (((y-offsetTop)+(currentLayerId()*(cubeSize/2)))  * 2 + (x-offsetLeft)) / cubeSize;
                }

                entry = I + J * size;

                if (entry < 0 || entry >= layers()[currentLayerId()].cubes().size())
                {
                    entry = -1;
                }
                else//DEBUG - DISPLAYING VALUES FOR TEST
                {
                    std::cout << "Entry n°" << entry << " - ";
                    std::cout << "[" << I << "; " << J << "]" << std::endl;
                }
            //}
            //END IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
        }

        return entry;
    }

I-J和entryNumber没问题,比如说对于立方体0,我有I=0;J=0;等等...这个是正常的。

我不明白为什么坐标范围像这张图片中红色部分(并非100%准确,我不是绘画天才哈哈):

enter image description here

但我应该得到的是(第二张图片-我点击的位置是红色部分) :

经过几次检查,我得到的I-J和entry相对应。这太奇怪了。

enter image description here

EDIT2:已实现偏移和图层号码。剩下的问题:错误的坐标范围。

以防万一,这是处理事件的'函数':

void GRAPHICS_HANDLER::listenEvents()
{
    while (window->pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        {
            window->close();
        }

        if(event.type == sf::Event::KeyPressed)
        {
            //DISPLAY/UNDISPLAY GRID -- DEBUG FUNCTION
            if(event.key.code == sf::Keyboard::Escape)
            {
                if(grid)
                    grid = false;
                else
                    grid = true;
            }

//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
            if(event.key.code == sf::Keyboard::B)//ACTIVE BRUSHMODE -- NEED TO BLOCK IT WHEN ACCESS VIOLATION OF CUBES ARRAY(CRASH)
            {
                if(!brushMode)
                {
                    brushMode = true;
                    std::cout << "Brush mode enabled" << std::endl;
                }
                else
                {
                    brushMode = false;
                    std::cout << "Brush mode disabled" << std::endl;
                }
            }

            if(event.key.code == sf::Keyboard::L)//ADD_LAYER
            {
                addLayer(getCurrentMapID());
            }

            if(event.key.code == sf::Keyboard::M)//DELETE_LAYER
            {
                deleteLayer(currentMapID, maps[currentMapID].currentLayerId());
            }

            if(event.key.code == sf::Keyboard::S)//ADD_LAYER
            {
                std::cout << "Select a texture: ";
                std::cin >> currentSelectedTexture; std::cout << std::endl;
            }

            if(event.key.code == sf::Keyboard::Left)//Move in Layer
            {
                if(maps[currentMapID].currentLayerId() > 0)
                {
                    maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()-1);
                }
            }

            if(event.key.code == sf::Keyboard::Right)//Move in Layer
            {
                if(maps[currentMapID].currentLayerId() < maps[currentMapID].layers().size()-1)
                {
                    maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()+1);
                }
            }
//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
        }

        if (event.type == sf::Event::MouseMoved)
        {
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
        }

        if (event.type == sf::Event::MouseButtonPressed)
        {
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseButton.x, event.mouseButton.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            if (event.mouseButton.button == sf::Mouse::Left)
            {
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
                if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
                {
                    cubeClicked = true;
                }
            }

            if (event.mouseButton.button == sf::Mouse::Right)
            {
                if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
                {
                    maps[currentMapID].layers()[maps[currentMapID].currentLayerId()].cubes()[currentSelectedCube].setTexture(1);
                }
            }
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
        }
    }
}

编辑3: 我更新了我的代码,使我可以仅绘制立方体的下侧,因此我可以画出这个(草地): 输入图像描述

当我放置一个平面正方形(绿色)时,坐标范围(前面在截图中显示的红色等距正方形)会稍微改变。我不知道为什么,但我想明确说明一下,以防万一。

1个回答

6
你需要存储每个瓦片平面元素的“高度”,以区分实际选择的立方体(越靠近观察者越近):

enter image description here

相同的屏幕坐标,但不同的瓦片。

我不清楚您如何建模您的世界,因此我将为您提供一个部分算法,以检查所点击的立方体的哪个面是哪个立方体的面。请根据您实际的代码和编写的类进行调整,使其正常工作。

// I'll let you to add the offsets for the screen coordinates
I = (y * 2 - x) / cubeSize;
J = (y * 2 + x) / cubeSize;
// find out if it is a left or right triangle
if ( x < (J - I) * (cubeSize/2) ) {
    // left triangle
    for ( k = max_n_layer; k > -1; --k ) {
        // you create the cubes nesting the I loop in the J loop, so to get the index of a cube,
        // assuming that you have created all the cubes (even the invisible ones, like it seems from your code)
        index = (J+1+k)*mapsize + I+1+k;

        // I don't really get how you define the existence or not of a face, but I guess something like this:
        if ( index < map.layer[k].cubes.size() 
            &&  map.layer[k].cubes[index].sides[top_side] != 0 ) { 
        // the face selected is the top side of cube[index] of layer k
            // you have to return index and k to select the right face, or simply a pointer to that face
            // if this makes any sense with how you have designed your model
            return &map.layer[k].cubes[index].sides[top_side];
        }
        // now check for the side
        index = (J+k)*mapsize + I+1+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[right_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[right_side];
        }
        index = (J+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[left_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[left_side];
        }
    }
} else {
    // right triangle
    for ( k = max_n_layer; k > -1; --k ) {

        index = (J+1+k)*mapsize + I+1+k;

        if ( index < map.layer[k].cubes.size() 
            &&  map.layer[k].cubes[index].sides[top_side] != 0 ) { 
            return &map.layer[k].cubes[index].sides[top_side];
        }

        index = (J+1+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[left_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[left_side];
        }
        index = (J+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[right_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[right_side];
        }
    }
}    
// well, no match found. As I said is up to you to decide how to do in this case
return nullptr;

编辑

我建议您尝试另一种方法。

将屏幕视为由您已描绘的三角形而非四边形瓷砖划分。您模型的每个2D瓦片都将由其中两个三角形组成,因此也是您想要绘制的立方体的所有面。对于每个立方体,不要绘制或甚至创建后面的面,这些面永远不会被绘制。

您可以尝试实现一种专门的z缓冲算法,通过存储在屏幕上必须绘制的每个三角形的更靠近观察者的侧面的索引来实现。所有三角形的顶点坐标(一次)都使用您已经拥有的代码计算。

            (I,J)              //For every node (I,J) you have a left and a right triangle
           .  *  .
(I+1,J) *  .  |  .  * (I,J+1)
              *
          (I+1,J+1)

你正在逐层创建立方体,每一层相对于基准平面的高度不同。使用之前计算出的坐标创建立方体的每一个面。对于每个面(仅限朝向观察者的3个面),考虑其2个三角形中的每一个。如果按顺序进行,您可以轻松确定它是否可见,然后只需更新相应三角形中存储的ID即可。
完成这个阶段后,您需要将每个三角形绘制一次,因为已经丢弃了隐藏的三角形。要确定从屏幕坐标到单元格索引的反转换,您只需计算击中的三角形,然后查找相应的ID。因此,将x、y转换回I、J(您已经拥有这些方程式),如果x < (J-I)/cubesize,则选择左边的三角形,否则选择右边的三角形。

我已经设置了“高度”,在我的所有代码中,“高度”都是层号。我所需要做的就是纠正I/J计算。 - Madz
谢谢您的回答,不幸的是,我不知道这是由于我的英语能力还是编程(数学?)能力不足,但我不确定是否理解它。我明白了我必须删除不需要显示的元素,但是什么是节点? - Madz
@Madz,从您的代码中,您会根据两个索引I和J计算每个立方体的x、y坐标。因此,每对索引(I,J)定义一个立方体。在您的绘图(以及我的绘图)中,每个三角形或四边形瓷砖的每个顶点都对应于一对索引(I,J)。您所做的数学运算是将这些值转换为屏幕坐标并进行反向转换。 - Bob__
@Madz,你可以在这个简短的教程或者这里找到一些帮助。 - Bob__
@Madz 我尝试根据我理解的你的代码(说实话并不是很多)编写了一些代码。希望它能在某种程度上对你有所帮助。 - Bob__
如果我理解你所写的内容:我可以用I/J获得一个边?你所写的真的很有帮助,而且你的编码方式比我的更清晰,当我阅读你的时候,我学会了以正确的方式编码。我只是不明白这些东西:节点,以及为什么我必须将某些东西绘制得更接近观察者,因为它是2D的。无论如何,感谢你的回答,真的很有帮助! - Madz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接