0
\$\begingroup\$

I am creating a 3d game. In this game I have a 3d model for a tile which I render in different locations using instancing.

I want to create a number of these tiles arranged so as to approximate a circle, similar to this:

Image showing a tile-based approximation to a circle.

I need to create an array of 3d vectors representing the position of the tiles in order to create this circle pattern with a given radius.

Here is the code I use to generate a square platform.

ENG_U is a constant representing a "Unit" in the game.

t is the array of positions passed to the renderer.

int rad = 16;

  t = new glm::vec3[(rad+rad+1)*(rad+rad+1)];
  int c = 0;
  for (int i =0;i < (rad+rad)+1;i++)
  {
    for (int j =0;j < (rad+rad)+1;j++)
    {
    t[c] = glm::vec3(i*ENG_U*1.7*2,0,j*ENG_U*1.7*2) - glm::vec3(ENG_U*1.7*2*rad,0,ENG_U*1.7*2*rad);
    c++;
    }
  }
\$\endgroup\$
  • \$\begingroup\$ If your tiles are squares, they aren't going to make a circle no matter what. You'll always end up with gaps or overlaps. \$\endgroup\$ – Draconis Jul 2 '17 at 4:25
  • \$\begingroup\$ But if you don't care about overlaps, polar coordinates are your friend. Use one for-loop for the radius (r), and another for the angle (theta), and place the tile at vec3(r×cos(theta)×ENG_U, 0, r×sin(theta)×ENG_U). \$\endgroup\$ – Draconis Jul 2 '17 at 4:27
  • \$\begingroup\$ (Stars replaced with × symbols due to Markdown formatting rules.) \$\endgroup\$ – Draconis Jul 2 '17 at 4:28
  • \$\begingroup\$ I mean making a circle in this fashion:2.bp.blogspot.com/-s4qeRc1nlk8/Tgq_A-viInI/AAAAAAAAAk4/… \$\endgroup\$ – Jon Jul 2 '17 at 4:29
  • \$\begingroup\$ Ahh, you should specify that in the question. It'll depend on the size of the circle more than usual. \$\endgroup\$ – Draconis Jul 2 '17 at 4:33
1
\$\begingroup\$

Same as the square except you check if the coordinate is within the circle's radius.

#include <vector>

int rad = 16;
float thicken = 0.5f; // between 0.0f and 1.0f
int thickened_radius2 = (int)floorf((rad + thicken) * (rad + thicken));

std::vector<glm::vec3> tiles;

tiles.reserve((rad*2+1)*(rad*2+1)); // (optional) reserve enough space for performance reason

for(int z = -rad; z <= rad; ++z){
  for(int x = -rad; x <= rad; ++x){
    if((x*x+z*z) <= thickened_radius2){ // check if tile is within circle
      tiles.push_back(glm::vec3(x, 0, z));
    }
  }
}

tiles.shrink_to_fit(); // (optional) only available in C++11 or newer

tiles.data() returns a pointer to the array (glm::vec3 *)

tiles.size() returns the length of the array (size_t)

If you need to copy that into a simple array:

#include <algorithm>


t = new glm::vec3[tiles.size()];
std::copy(tiles.begin(), tiles.end(), t);

But I'd just keep it in an std::vector this way you have the size() readily available. The overhead of std::vector is negligible.

thicken is optional but if you don't use this you'll only have 1 tile at each axis "side" of the circle. It makes a prettier circle. (Set it to 0 and see for yourself.)

\$\endgroup\$
0
\$\begingroup\$

Here is the algorithm to generate the tile offsets.

  std::vector<glm::vec3> ps;
  int rad = 16;
  int r2 = 32;
  int c = 0;
  for (int i =0;i < (rad+rad)+1;i++)
  {
    for (int j =0;j < (rad+rad)+1;j++)
    {
      glm::vec3 p = glm::vec3(i*ENG_U*1.7*2,0,j*ENG_U*1.7*2) - glm::vec3(ENG_U*1.7*2*rad,0,ENG_U*1.7*2*rad);
      bool rm = false;
      if (length(p+glm::vec3(ENG_U*1.7,0,ENG_U*1.7)) > ENG_U*1.7*r2)
      rm = true;
      if (length(p-glm::vec3(ENG_U*1.7,0,ENG_U*1.7)) > ENG_U*1.7*r2)
      rm = true;
      if (length(p+glm::vec3(-ENG_U*1.7,0,ENG_U*1.7)) > ENG_U*1.7*r2)
      rm = true;
      if (length(p+glm::vec3(ENG_U*1.7,0,-ENG_U*1.7)) > ENG_U*1.7*r2)
      rm = true;
      if(!rm)
     ps.push_back(p);
    c++;
    }
  }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.