Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[Irrlicht] Generowanie trójwymiarowej siatki heksagonalnej

Ostatnio zmodyfikowano 2024-03-20 14:01
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[Irrlicht] Generowanie trójwymiarowej siatki heksagonalnej
» 2024-03-03 21:37:13
Aby dodać wysokość do mapy heksagonalnej (sześciokątnej) należy podzielić ją na składowe i na nich operować. Między kafelkami mapy powstaną wtedy tarasy (terraces) dzięki którym osiągniemy łagone i płynne przejście z jednego do drugiego kafelka. Tym sposobem dzielimy sześciokąt na sześć trójkątów wychodzących z jego środka, a następnie każdy taki trójkąt dzielimy na trójkąt wewnętrzny oraz  quad zewnętrzny. Trójkąt wewnętrzny będzie miał teksturę odpowiednią dla danego pola, zaś quad zewnętrzny będzie zawierał zarówno teksturę własnego kafelka jak i sąsiedniego.


Istotne zmienne dla siatki:
C/C++
float outerRadius = 10.0f;
float innerRadius = outerRadius * 0.866025404f;
float heightStep = 6.0f;
float tileBaseFactor = 0.8f;

float hexVertices[ ] =
{
   
0.0f, outerRadius,
   
innerRadius, outerRadius * 0.5f,
   
innerRadius, - outerRadius * 0.5f,
   
0.0f, - outerRadius,
   
- innerRadius, - outerRadius * 0.5f,
   
- innerRadius, outerRadius * 0.5f,
   
0.0f, outerRadius
};

const u16 hexIndices[ 18 ] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0, 6, 1 };

float texture_uv[ ]
{
   
0.0f, 0.5f,
   
0.5f, 0.25f,
   
0.5f, - 0.25f,
   
0.0f, - 0.5f,
   
- 0.5f, - 0.25f,
   
- 0.5f, 0.25f,
   
0.0f, 0.5f,
};

render, kolizje oraz tworzenie mesh:
C/C++
void HexTile::render()
{
   
IVideoDriver * driver = smgr->getVideoDriver();
   
driver->setMaterial( baseMeshBuffer->Material );
   
driver->setTransform( video::ETS_WORLD, AbsoluteTransformation );
   
driver->drawMeshBuffer( baseMeshBuffer );
   
   
for( int i = 0; i < 6; i++ )
   
{
       
driver->setMaterial( sidesMeshBuffer[ i ]->Material );
       
driver->setTransform( video::ETS_WORLD, AbsoluteTransformation );
       
driver->drawMeshBuffer( sidesMeshBuffer[ i ] );
   
}
   
}

// ...

void HexTile::generateTerrainMesh()
{
   
SColor color = SColor( 255, 255, 255, 255 ); // SET THE VERTICES
   
    ////////////////////////////////////////////////////////////////////////////////
    // TILE BASE
   
   
baseMeshBuffer = new SMeshBuffer();
   
   
baseMeshBuffer->Indices.set_used( 18 ); // SET THE INDICES
   
for( u32 i = 0; i < 18; ++i )
       
 baseMeshBuffer->Indices[ i ] = hexIndices[ i ];
   
   
   
baseMeshBuffer->Vertices.push_back( S3DVertex( 0, 0, 0, 0, 0, 0, color, 0, 0 ) ); // SET THE VERTICES
   
for( int i = 0; i < 6; i++ )
   
{
       
baseMeshBuffer->Vertices.push_back( S3DVertex( hexVertices[ 2 * i ] * tileBaseFactor, 0.0f, hexVertices[ 2 * i + 1 ] * tileBaseFactor, 0, 0, 0, color, texture_uv[ 2 * i ], texture_uv[ 2 * i + 1 ] ) );
       
baseMeshBuffer->BoundingBox.addInternalPoint( vector3df( hexVertices[ 2 * i ] * tileBaseFactor, 0, hexVertices[ 2 * i + 1 ] * tileBaseFactor ) );
   
}
   
   
baseMeshBuffer->Material.Wireframe = false; // SET THE MATERIAL
   
baseMeshBuffer->Material.Lighting = false;
   
baseMeshBuffer->Material.setTexture( 0, getTexture( ttype ) );
   
   
baseMesh = new SMesh(); // CREATE MESH
   
baseMesh->addMeshBuffer( baseMeshBuffer );
   
baseMesh->recalculateBoundingBox();
   
   
////////////////////////////////////////////////////////////////////////////////
    // TILE SIDES
   
   
   
for( int i = 0; i < 6; i++ )
   
{
       
sidesMeshBuffer[ i ] = new SMeshBuffer(); // SET THE INDICES
       
sidesMeshBuffer[ i ]->Indices.set_used( 6 );
       
sidesMeshBuffer[ i ]->Indices[ 0 ] = 0;
       
sidesMeshBuffer[ i ]->Indices[ 1 ] = 1;
       
sidesMeshBuffer[ i ]->Indices[ 2 ] = 2;
       
sidesMeshBuffer[ i ]->Indices[ 3 ] = 0;
       
sidesMeshBuffer[ i ]->Indices[ 4 ] = 2;
       
sidesMeshBuffer[ i ]->Indices[ 5 ] = 3;
       
       
float h;
       
if( neighbours[ i ] != nullptr )
           
 h = float( neighbours[ i ]->height - this->height ) * heightStep * 0.5f;
       
else
           
 h = 0.0f;
       
       
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 * i ] * tileBaseFactor, 0.0f, hexVertices[ 2 * i + 1 ] * tileBaseFactor, 0, 0, 0, color, 0, 0 ) ); // SET THE VERTICES
       
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 * i ], h, hexVertices[ 2 * i + 1 ], 0, 0, 0, color, texture_uv[ 2 * i ], texture_uv[ 2 * i + 1 ] ) );
       
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 *( i + 1 ) ], h, hexVertices[ 2 *( i + 1 ) + 1 ], 0, 0, 0, color, texture_uv[ 2 * i ], texture_uv[ 2 * i + 1 ] ) );
       
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 *( i + 1 ) ] * tileBaseFactor, 0.0f, hexVertices[ 2 *( i + 1 ) + 1 ] * tileBaseFactor, 0, 0, 0, color, 0, 0 ) );
       
       
sidesMeshBuffer[ i ]->Material.Wireframe = false; // SET THE MATERIAL
       
sidesMeshBuffer[ i ]->Material.Lighting = false;
       
sidesMeshBuffer[ i ]->Material.setTexture( 0, getTexture( ttype ) );
       
       
sidesMesh[ i ] = new SMesh(); // CREATE MESH
       
sidesMesh[ i ]->addMeshBuffer( sidesMeshBuffer[ i ] );
       
sidesMesh[ i ]->recalculateBoundingBox();
   
}
   
   
vector3df pos = hexToGlobal( x, z ); // SET THE POSITION OF NODE
   
pos.Y = float( height ) * heightStep;
   
setPosition( pos );
   
}

//...

void HexMap::addTriangleSelector()
{
   
// ADDING TRIANGLE SELECTOR TO MAP ( COLLISIONS )
   
ITriangleSelector * selector;
   
for( auto & t: tiles )
   
{
       
selector = smgr->createTriangleSelector( t->baseMesh, t );
       
t->setTriangleSelector( selector );
       
selector->drop();
   
}
}

do poprawy:
-współrzędne tekstur dla boków kafelków ( jakoś obliczyć tylko jak ? )
-napisać łączenie kafelków ( terraces ) i skąd się biorą te "puste" trójkąty ?
-napisać kolizje boków kafelków z kursorem (można się obejść bez ale wypadałoby )
-optymalizacja ( mapa 40x40, zaledwie 75 FPS - spadek 300 FPS - cel to mapa 100x80 )
-mapa jest małoczytelna, wypadałoby dla poprawy przejrzystości dodać chociaż cieniowanie

próbuję to odwzorować ( bez cyferek rzecz jasna ;-) )
P-180907
pekfos
» 2024-03-04 17:40:15
Geometria na obrazku referencyjnym nie zgadza się z koncepcją na pierwszym rysunku. Zauważ że w miejscu gdzie stykają się 3 sześciokąty masz 1 trójkąt, a w u ciebie schodzą się w punkt. Koncepcja przejść między kaflami wydaje mi się nie mieć sensu. Przykładowo woda nie wspina się po ścianie do połowy wysokości, tylko raczej zostaje na dole. Może byłoby prościej i bardziej realistycznie jakby przyjąć że kafel o większej wysokości dyktuje teksturę stoku. Ustawianie przejścia między teksturami na połowę wysokości może też wyglądać dziwnie gdy masz duży rozstrzał różnic wysokości, jak na przykład na załączonym obrazku referencyjnym.

-współrzędne tekstur dla boków kafelków ( jakoś obliczyć tylko jak ? )
Efekt wygląda jakbyś współrzędne tekstury miał ustawione dla wewnętrznego sześciokąta, a zewnętrzny powtarza 1 piksel. Powinieneś raczej mierzyć teksturę by pokrywała się z zewnętrznym sześciokątem.
P-180909
tBane
Temat założony przez niniejszego użytkownika
» 2024-03-05 03:55:35
Geometria na obrazku referencyjnym nie zgadza się z koncepcją na pierwszym rysunku. Zauważ że w miejscu gdzie stykają się 3 sześciokąty masz 1 trójkąt, a w u ciebie schodzą się w punkt.
No właśnie to zauważyłem, po wygenerowaniu mapy. Gdy wysokość trzech kafelków nie jest taka sama generuje się dodatkowy trójkąt.
A wodę planuję inaczej dodać. Każdy kafelek będzie posiadał informację czy zawiera wodę, a jeżeli tak to woda będzie generowana z pół wysokości wyżej jako sześciokątny shader. ( drawHexWater(x, y+waterHeight, z); )

Na razie próbuję w ogóle jakoś ogarnąć, by tekstury rysowało, póżniej będę to pielęgnował, ale najpierw musi działać.

P-180910
tBane
Temat założony przez niniejszego użytkownika
» 2024-03-05 04:34:25
Dokładniej przyjrzałem się generowanej mapie i zlokalizowałem błąd. Jednak nie generują się dodatkowe trójkąty a źle obliczana jest wysokość dla boków sześciokątów. Próbowałem dla lewej oraz prawej strony wyliczyć wysokość w ten sposób ale coś nie działa:


C/C++
float hleft, hright;
int h1, h2, h3;

h1 = height;
if( neighbours[ getDirection( i - 1 ) ] != nullptr )
   
 h2 = neighbours[ getDirection( i - 1 ) ]->height;
else
   
 h2 = h1;

if( neighbours[ getDirection( i ) ] != nullptr )
   
 h3 = neighbours[ getDirection( i ) ]->height;
else
   
 h3 = h1;

hleft = float( max( h1, h2, h3 ) - min( h1, h2, h3 ) );

h1 = height;
if( neighbours[ getDirection( i + 1 ) ] != nullptr )
   
 h2 = neighbours[ getDirection( i + 1 ) ]->height;
else
   
 h2 = h1;

if( neighbours[ getDirection( i ) ] != nullptr )
   
 h3 = neighbours[ getDirection( i ) ]->height;
else
   
 h3 = h1;

hright = float( max( h1, h2, h3 ) - min( h1, h2, h3 ) );


sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 * i ] * tileBaseFactor, height * heightStep, hexVertices[ 2 * i + 1 ] * tileBaseFactor, 0, 0, 0, color, 0, 0 ) ); // SET THE VERTICES
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 * i ], hleft * heightStep, hexVertices[ 2 * i + 1 ], 0, 0, 0, color, texture_uv[ 2 * i ], texture_uv[ 2 * i + 1 ] ) );
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 *( i + 1 ) ], hright * heightStep, hexVertices[ 2 *( i + 1 ) + 1 ], 0, 0, 0, color, texture_uv[ 2 * i ], texture_uv[ 2 * i + 1 ] ) );
sidesMeshBuffer[ i ]->Vertices.push_back( S3DVertex( hexVertices[ 2 *( i + 1 ) ] * tileBaseFactor, height * heightStep, hexVertices[ 2 *( i + 1 ) + 1 ] * tileBaseFactor, 0, 0, 0, color, 0, 0 ) );
// ...

P-180911
pekfos
» 2024-03-05 17:54:57
C/C++
hright = float( max( h1, h2, h3 ) - min( h1, h2, h3 ) );
Nie powinna to być średnia?
P-180918
tBane
Temat założony przez niniejszego użytkownika
» 2024-03-05 21:59:21
Jeżeli są to 3 pola obok siebie o wysokości 3, 3 i 1 to wysokość średnia wynosi 2 czyli max(3,3,1) - min(3,3,1) = 3 - 1 = 2 a nie 7/3
P-180919
pekfos
» 2024-03-05 22:11:01
Średnia zamiast samej różnicy. Bo teraz 8,8,6 da też 2 zamiast 7.
C/C++
hright = 0.5f *( max( h1, h2, h3 ) + min( h1, h2, h3 ) );
P-180920
tBane
Temat założony przez niniejszego użytkownika
» 2024-03-06 02:30:07
Teraz działa ;-)
Ale jednak nie tak jak powinno - złą metodę wymyśliłem. Quady wystają tworząc trójkąty prostopadłe do kafelka jak na obrazku poniżej. Czyli chyba wracamy do pierwotnej metody (drugi obrazek) i trzeba jakoś znaleźć te trójkąty.

C/C++
float h;
if( neighbours[ i ] != nullptr )
   
 h = float( neighbours[ i ]->height + this->height ) * 0.5f;
else
   
 h = height;

P-180921
« 1 » 2 3 4
  Strona 1 z 4 Następna strona