6d facedir
authorRealBadAngel <mk@realbadangel.pl>
Sat, 23 Mar 2013 18:17:00 +0000 (19:17 +0100)
committerPilzAdam <pilzadam@minetest.net>
Sat, 23 Mar 2013 20:31:05 +0000 (21:31 +0100)
doc/lua_api.txt
src/content_mapblock.cpp
src/mapblock_mesh.cpp
src/mapnode.cpp
src/tile.h

index af8b1cc9a585a7a0db9f1cc3c07e84c2cc8983df..3d47785bad4df7354b1c9d6e1ae3453372af2b8f 100644 (file)
@@ -310,6 +310,10 @@ param2 is reserved for the engine when any of these are used:
   paramtype2 == "facedir"
   ^ The rotation of the node is stored in param2. Furnaces and chests are
     rotated this way. Can be made by using minetest.dir_to_facedir().
+    Values range 0 - 23
+    facedir modulo 4 = axisdir
+    0 = y+    1 = z+    2 = z-    3 = x+    4 = x-    5 = y-
+    facedir's two less significant bits are rotation around the axis
 
 Nodes can also contain extra data. See "Node Metadata".
 
index 0fbdda303fb2fcf611d1f98a2ef2bd9c17e4851a..84d5f067c90704c928b8e7985b44b346e8676839 100644 (file)
@@ -42,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 //              (compatible with ContentFeatures). If you specified 0,0,1,1
 //              for each face, that would be the same as passing NULL.
 void makeCuboid(MeshCollector *collector, const aabb3f &box,
-       const TileSpec *tiles, int tilecount,
+       TileSpec *tiles, int tilecount,
        video::SColor &c, const f32* txc)
 {
        assert(tilecount >= 1 && tilecount <= 6);
 
        v3f min = box.MinEdge;
        v3f max = box.MaxEdge;
-
        if(txc == NULL)
        {
                static const f32 txc_default[24] = {
@@ -97,15 +99,70 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
                video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
        };
 
-       for(s32 j=0; j<24; j++)
+       v2f t;
+       for(int i = 0; i < tilecount; i++)
+                               {
+                               switch (tiles[i].rotation)
+                               {
+                               case 0:
+                                       break;
+                               case 1: //R90
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+                                       break;
+                               case 2: //R180
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
+                                       break;
+                               case 3: //R270
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+                                       break;
+                               case 4: //FXR90
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+
+                                       tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+                                       tiles[i].texture.size.Y *= -1;
+                                       break;
+                               case 5: //FXR270
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+                                       t=vertices[i*4].TCoords;
+                                       tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+                                       tiles[i].texture.size.Y *= -1;
+                                       break;
+                               case 6: //FYR90
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+                                       tiles[i].texture.pos.X += tiles[i].texture.size.X;
+                                       tiles[i].texture.size.X *= -1;
+                                       break;
+                               case 7: //FYR270
+                                       for (int x = 0; x < 4; x++)
+                                               vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+                                       tiles[i].texture.pos.X += tiles[i].texture.size.X;
+                                       tiles[i].texture.size.X *= -1;
+                                       break;
+                               case 8: //FX
+                                       tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+                                       tiles[i].texture.size.Y *= -1;
+                                       break;
+                               case 9: //FY
+                                       tiles[i].texture.pos.X += tiles[i].texture.size.X;
+                                       tiles[i].texture.size.X *= -1;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+               for(s32 j=0; j<24; j++)
        {
                int tileindex = MYMIN(j/4, tilecount-1);
                vertices[j].TCoords *= tiles[tileindex].texture.size;
                vertices[j].TCoords += tiles[tileindex].texture.pos;
        }
-
        u16 indices[] = {0,1,2,2,3,0};
-
        // Add to mesh collector
        for(s32 j=0; j<24; j+=4)
        {
@@ -1154,14 +1211,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                v3s16(0, 0, 1),
                                v3s16(0, 0, -1)
                        };
-
                        TileSpec tiles[6];
-                       for(int i = 0; i < 6; i++)
-                       {
-                               // Handles facedir rotation for textures
-                               tiles[i] = getNodeTile(n, p, tile_dirs[i], data);
-                       }
-
+                       
                        u16 l = getInteriorLight(n, 0, data);
                        video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
 
@@ -1172,17 +1223,43 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        i = boxes.begin();
                                        i != boxes.end(); i++)
                        {
+                       for(int j = 0; j < 6; j++)
+                               {
+                               // Handles facedir rotation for textures
+                               tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
+                               }
                                aabb3f box = *i;
                                box.MinEdge += pos;
                                box.MaxEdge += pos;
+                               
+                               f32 temp;
+                               if (box.MinEdge.X > box.MaxEdge.X)
+                               {
+                                       temp=box.MinEdge.X;
+                                       box.MinEdge.X=box.MaxEdge.X;
+                                       box.MaxEdge.X=temp;
+                               }
+                               if (box.MinEdge.Y > box.MaxEdge.Y)
+                               {
+                                       temp=box.MinEdge.Y;
+                                       box.MinEdge.Y=box.MaxEdge.Y;
+                                       box.MaxEdge.Y=temp;
+                               }
+                               if (box.MinEdge.Z > box.MaxEdge.Z)
+                               {
+                                       temp=box.MinEdge.Z;
+                                       box.MinEdge.Z=box.MaxEdge.Z;
+                                       box.MaxEdge.Z=temp;
+                               }
 
+                               //
                                // Compute texture coords
-                               f32 tx1 = (i->MinEdge.X/BS)+0.5;
-                               f32 ty1 = (i->MinEdge.Y/BS)+0.5;
-                               f32 tz1 = (i->MinEdge.Z/BS)+0.5;
-                               f32 tx2 = (i->MaxEdge.X/BS)+0.5;
-                               f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
-                               f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
+                               f32 tx1 = (box.MinEdge.X/BS)+0.5;
+                               f32 ty1 = (box.MinEdge.Y/BS)+0.5;
+                               f32 tz1 = (box.MinEdge.Z/BS)+0.5;
+                               f32 tx2 = (box.MaxEdge.X/BS)+0.5;
+                               f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
+                               f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
                                f32 txc[24] = {
                                        // up
                                        tx1, 1-tz2, tx2, 1-tz1,
@@ -1197,7 +1274,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        // front
                                        tx1, 1-ty2, tx2, 1-ty1,
                                };
-
                                makeCuboid(&collector, box, tiles, 6, c, txc);
                        }
                break;}
index d098065f8f64d758633bac1dc4b7e03b3bd29e01..f68a79e411c77f88ee5a41c9788e7af3dad31052 100644 (file)
@@ -455,6 +455,80 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
        v3f vertex_pos[4];
        v3s16 vertex_dirs[4];
        getNodeVertexDirs(dir, vertex_dirs);
+       v3s16 t;
+       switch (tile.rotation)
+       {
+       case 0:
+               break;
+       case 1: //R90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               break;
+       case 2: //R180
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[2];
+               vertex_dirs[2] = t;
+               t = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               break;
+       case 3: //R270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               break;
+       case 4: //FXR90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               tile.texture.pos.Y += tile.texture.size.Y;
+               tile.texture.size.Y *= -1;
+               break;
+       case 5: //FXR270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               tile.texture.pos.Y += tile.texture.size.Y;
+               tile.texture.size.Y *= -1;
+               break;
+       case 6: //FYR90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               tile.texture.pos.X += tile.texture.size.X;
+               tile.texture.size.X *= -1;
+               break;
+       case 7: //FYR270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               tile.texture.pos.X += tile.texture.size.X;
+               tile.texture.size.X *= -1;
+               break;
+       case 8: //FX
+               tile.texture.pos.Y += tile.texture.size.Y;
+               tile.texture.size.Y *= -1;
+               break;
+       case 9: //FY
+               tile.texture.pos.X += tile.texture.size.X;
+               tile.texture.size.X *= -1;
+               break;
+       default:
+               break;
+       }
        for(u16 i=0; i<4; i++)
        {
                vertex_pos[i] = v3f(
@@ -601,60 +675,50 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
        //  5 = (0,0,-1)
        //  6 = (0,-1,0)
        //  7 = (-1,0,0)
-       u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
+       u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
 
        // Get rotation for things like chests
        u8 facedir = mn.getFaceDir(ndef);
-       assert(facedir <= 3);
-       
-       static const u8 dir_to_tile[4 * 8] =
+       assert(facedir <= 23);
+       static const u16 dir_to_tile[24 * 16] =
        {
-               // 0  +X  +Y  +Z   0  -Z  -Y  -X
-                  0,  2,  0,  4,  0,  5,  1,  3,  // facedir = 0
-                  0,  4,  0,  3,  0,  2,  1,  5,  // facedir = 1
-                  0,  3,  0,  5,  0,  4,  1,  2,  // facedir = 2
-                  0,  5,  0,  2,  0,  3,  1,  4,  // facedir = 3
+               // 0     +X    +Y    +Z           -Z    -Y    -X   ->   value=tile,rotation  
+                  0,0,  2,0 , 0,0 , 4,0 ,  0,0,  5,0 , 1,0 , 3,0 ,  // rotate around y+ 0 - 3
+                  0,0,  4,0 , 0,3 , 3,0 ,  0,0,  2,0 , 1,1 , 5,0 ,
+                  0,0,  3,0 , 0,2 , 5,0 ,  0,0,  4,0 , 1,2 , 2,0 ,
+                  0,0,  5,0 , 0,1 , 2,0 ,  0,0,  3,0 , 1,3 , 4,0 ,
+
+                  0,0,  2,3 , 5,0 , 0,2 ,  0,0,  1,0 , 4,2 , 3,1 ,  // rotate around z+ 4 - 7
+                  0,0,  4,3 , 2,0 , 0,3 ,  0,0,  1,1 , 3,2 , 5,1 ,
+                  0,0,  3,3 , 4,0 , 0,0 ,  0,0,  1,2 , 5,2 , 2,1 ,
+                  0,0,  5,3 , 3,0 , 0,1 ,  0,0,  1,3 , 2,2 , 4,1 ,
+
+                  0,0,  2,1 , 4,2 , 1,2 ,  0,0,  0,0 , 5,0 , 3,3 ,  // rotate around z- 8 - 11
+                  0,0,  4,1 , 3,2 , 1,3 ,  0,0,  0,3 , 2,0 , 5,3 ,
+                  0,0,  3,1 , 5,2 , 1,0 ,  0,0,  0,2 , 4,0 , 2,3 ,
+                  0,0,  5,1 , 2,2 , 1,1 ,  0,0,  0,1 , 3,0 , 4,3 ,
+
+                  0,0,  0,3 , 3,3 , 4,1 ,  0,0,  5,3 , 2,3 , 1,3 ,  // rotate around x+ 12 - 15
+                  0,0,  0,2 , 5,3 , 3,1 ,  0,0,  2,3 , 4,3 , 1,0 ,
+                  0,0,  0,1 , 2,3 , 5,1 ,  0,0,  4,3 , 3,3 , 1,1 ,
+                  0,0,  0,0 , 4,3 , 2,1 ,  0,0,  3,3 , 5,3 , 1,2 ,
+                  
+                  0,0,  1,1 , 2,1 , 4,3 ,  0,0,  5,1 , 3,1 , 0,1 ,  // rotate around x- 16 - 19  
+                  0,0,  1,2 , 4,1 , 3,3 ,  0,0,  2,1 , 5,1 , 0,0 ,
+                  0,0,  1,3 , 3,1 , 5,3 ,  0,0,  4,1 , 2,1 , 0,3 ,  
+                  0,0,  1,0 , 5,1 , 2,3 ,  0,0,  3,1 , 4,1 , 0,2 ,  
+               
+                  0,0,  3,2 , 1,2 , 4,2 ,  0,0,  5,2 , 0,2 , 2,2 ,  // rotate around y- 20 - 23
+                  0,0,  5,2 , 1,3 , 3,2 ,  0,0,  2,2 , 0,1 , 4,2 ,  
+                  0,0,  2,2 , 1,0 , 5,2 ,  0,0,  4,2 , 0,0 , 3,2 ,  
+                  0,0,  4,2 , 1,1 , 2,2 ,  0,0,  3,2 , 0,3 , 5,2   
+                  
        };
-       u8 tileindex = dir_to_tile[facedir*8 + dir_i];
-
-       // If not rotated or is side tile, we're done
-       if(facedir == 0 || (tileindex != 0 && tileindex != 1))
-               return getNodeTileN(mn, p, tileindex, data);
-
-       // This is the top or bottom tile, and it shall be rotated; thus rotate it
-       TileSpec spec = getNodeTileN(mn, p, tileindex, data);
-       if(tileindex == 0){
-               if(facedir == 1){ // -90
-                       std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-                       name += "^[transformR270";
-                       spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-               }
-               else if(facedir == 2){ // 180
-                       spec.texture.pos += spec.texture.size;
-                       spec.texture.size *= -1;
-               }
-               else if(facedir == 3){ // 90
-                       std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-                       name += "^[transformR90";
-                       spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-               }
-       }
-       else if(tileindex == 1){
-               if(facedir == 1){ // -90
-                       std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-                       name += "^[transformR90";
-                       spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-               }
-               else if(facedir == 2){ // 180
-                       spec.texture.pos += spec.texture.size;
-                       spec.texture.size *= -1;
-               }
-               else if(facedir == 3){ // 90
-                       std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-                       name += "^[transformR270";
-                       spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-               }
-       }
+       u16 tile_index=facedir*16 + dir_i;
+       TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
+       spec.rotation=dir_to_tile[tile_index + 1];
+       std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+       spec.texture = data->m_gamedef->tsrc()->getTexture(name);
        return spec;
 }
 
@@ -794,6 +858,7 @@ static void updateFastFaceRow(
                                        && next_lights[2] == lights[2]
                                        && next_lights[3] == lights[3]
                                        && next_tile == tile
+                                       && tile.rotation == 0
                                        && next_light_source == light_source)
                        {
                                next_is_different = false;
index 0513e688c399debf17d235837e5c9eefa911845d..bba845fcce95678dffae5f033d96d554a911d681 100644 (file)
@@ -107,7 +107,7 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
 {
        const ContentFeatures &f = nodemgr->get(*this);
        if(f.param_type_2 == CPT2_FACEDIR)
-               return getParam2() & 0x03;
+               return getParam2() & 0x1F;
        return 0;
 }
 
@@ -140,29 +140,131 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
        {
                const std::vector<aabb3f> &fixed = nodebox.fixed;
                int facedir = n.getFaceDir(nodemgr);
+               u8 axisdir = facedir>>2;
+               facedir&=0x03;
                for(std::vector<aabb3f>::const_iterator
                                i = fixed.begin();
                                i != fixed.end(); i++)
                {
                        aabb3f box = *i;
-                       if(facedir == 1)
+                       switch (axisdir)
                        {
-                               box.MinEdge.rotateXZBy(-90);
-                               box.MaxEdge.rotateXZBy(-90);
-                               box.repair();
-                       }
-                       else if(facedir == 2)
-                       {
-                               box.MinEdge.rotateXZBy(180);
-                               box.MaxEdge.rotateXZBy(180);
-                               box.repair();
-                       }
-                       else if(facedir == 3)
-                       {
-                               box.MinEdge.rotateXZBy(90);
-                               box.MaxEdge.rotateXZBy(90);
-                               box.repair();
+                       case 0:
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateXZBy(-90);
+                                       box.MaxEdge.rotateXZBy(-90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateXZBy(180);
+                                       box.MaxEdge.rotateXZBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateXZBy(90);
+                                       box.MaxEdge.rotateXZBy(90);
+                               }
+                               break;
+                       case 1: // z+
+                               box.MinEdge.rotateYZBy(90);
+                               box.MaxEdge.rotateYZBy(90);
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateXYBy(90);
+                                       box.MaxEdge.rotateXYBy(90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateXYBy(180);
+                                       box.MaxEdge.rotateXYBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateXYBy(-90);
+                                       box.MaxEdge.rotateXYBy(-90);
+                               }
+                               break;
+                       case 2: //z-
+                               box.MinEdge.rotateYZBy(-90);
+                               box.MaxEdge.rotateYZBy(-90);
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateXYBy(-90);
+                                       box.MaxEdge.rotateXYBy(-90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateXYBy(180);
+                                       box.MaxEdge.rotateXYBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateXYBy(90);
+                                       box.MaxEdge.rotateXYBy(90);
+                               }
+                               break;
+                       case 3:  //x+
+                               box.MinEdge.rotateXYBy(-90);
+                               box.MaxEdge.rotateXYBy(-90);
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateYZBy(90);
+                                       box.MaxEdge.rotateYZBy(90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateYZBy(180);
+                                       box.MaxEdge.rotateYZBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateYZBy(-90);
+                                       box.MaxEdge.rotateYZBy(-90);
+                               }
+                               break;
+                       case 4:  //x-
+                               box.MinEdge.rotateXYBy(90);
+                               box.MaxEdge.rotateXYBy(90);
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateYZBy(-90);
+                                       box.MaxEdge.rotateYZBy(-90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateYZBy(180);
+                                       box.MaxEdge.rotateYZBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateYZBy(90);
+                                       box.MaxEdge.rotateYZBy(90);
+                               }
+                               break;
+                       case 5:
+                               box.MinEdge.rotateXYBy(-180);
+                               box.MaxEdge.rotateXYBy(-180);
+                               if(facedir == 1)
+                               {
+                                       box.MinEdge.rotateXZBy(90);
+                                       box.MaxEdge.rotateXZBy(90);
+                               }
+                               else if(facedir == 2)
+                               {
+                                       box.MinEdge.rotateXZBy(180);
+                                       box.MaxEdge.rotateXZBy(180);
+                               }
+                               else if(facedir == 3)
+                               {
+                                       box.MinEdge.rotateXZBy(-90);
+                                       box.MaxEdge.rotateXZBy(-90);
+                               }
+                               break;
+                       default:
+                               break;
                        }
+                       box.repair();
                        boxes.push_back(box);
                }
        }
index 07e5bcb5693eb802f69a24dd1c1c6eaf2125a543..c5c7f930346eef67859078355a41c8ae5e5b9c26 100644 (file)
@@ -205,7 +205,8 @@ struct TileSpec
                        texture == other.texture &&
                        alpha == other.alpha &&
                        material_type == other.material_type &&
-                       material_flags == other.material_flags
+                       material_flags == other.material_flags &&
+                       rotation == other.rotation
                );
        }
 
@@ -264,6 +265,7 @@ struct TileSpec
        // Animation parameters
        u8 animation_frame_count;
        u16 animation_frame_length_ms;
+       u8 rotation;
 };
 
 #endif