Message Boards Message Boards

Converting chess OBJ files to voxels

Posted 3 years ago
POSTED BY: Brad Klee
8 Replies
Posted 3 years ago
POSTED BY: Brad Klee

Super-awesome! Is this viable for Second Life, Minecraft, Unity, Unreal, Meta, or other 3D worlds environments coming at us rapidly?

POSTED BY: Kapio Letto

enter image description here -- you have earned Featured Contributor Badge enter image description here Your exceptional post has been selected for our editorial column Staff Picks http://wolfr.am/StaffPicks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: EDITORIAL BOARD

Dear Brad,

It's very interesting and beautiful design, Thank you for share.

I was inspired by Bauhaus Chess Set, so I also designed a Minimalism version. I upload a few image for you to know the concept, the packing size will also be minimal. Later I build it by Lego with my daughter and played for a while. We have fun.

enter image description here enter image description here enter image description here enter image description here enter image description here

POSTED BY: Frederick Wu
Posted 3 years ago

For a true vox file we also need to list colors. This becomes more important when the particular asset has more than two colors. Here is revised export function and an import function to pair with it:

VoxFileFormat[verts_, objname_, SlabSize_, DefaultCols_] := Join[{
   StringJoin["Vox file for ", objname],
   StringJoin[SlabSize, " slab size "],
   "---------------------",
   " x    y    z    c    ",
   "---------------------"},
  Flatten[MapIndexed[Function[{vert},
        Append[vert, #2[[1]] ]] /@ #1 &, verts], 
    1] /. {x_, y_, z_, c_} :> StringJoin[
     If[Sign[x] == 1, " ", "-"],
     StringPadRight[ToString[Abs[x]], 4, " "],
     If[Sign[y] == 1, " ", "-"],
     StringPadRight[ToString[Abs[y]], 4, " "],
     If[Sign[z] == 1, " ", "-"],
     StringPadRight[ToString[Abs[z]], 4, " "],
     If[Sign[c] == 1, " ", "-"],
     StringPadRight[ToString[c], 2, " "]
     ], {
   "---------------------",
   " c    R    G    B    ",
   "---------------------"},
  MapIndexed[StringJoin[
     " ", StringPadRight[ToString[#2[[1]]], 4, " "],
     " ", StringPadRight[ToString[#1[[1]]], 4, " "],
     " ", StringPadRight[ToString[#1[[2]]], 4, " "],
     " ", StringPadRight[ToString[#1[[3]]], 4, " "]
     ] &, DefaultCols]
  ]

ToCubes[verts_, cols_] :=  With[
  {colRep = Rule[#[[1]], RGBColor[#[[2 ;; 4]]/255]] & /@ cols},
  {#[[4]] /. colRep, Cuboid[#[[1 ;; 3]]]} & /@ verts]

ImportVox[file_] := With[{lines = StringSplit[Import[file], "\n"]},
  Function[{markers},    ToCubes[ Map[ToExpression, DeleteCases[
       StringSplit[lines[[ markers[[2]] + 1 ;; markers[[3]] - 1  ]], 
        " "], "", Infinity], {2}], Map[ToExpression, 
      DeleteCases[ StringSplit[lines[[ markers[[4]] + 1 ;; -1  ]], " "], "", 
       Infinity], {2}]] ][Flatten[Position[lines, "---------------------"]]]]

I then used the export function to create new files and push to github. Then we can import:

GameBoard =  Graphics3D[{EdgeForm[None], 
   ImportVox["~/OpenAssets/WorldChess/TXT/OYChessboardVox.txt"]}, 
  Boxed -> False, ImageSize -> 1000,
  ViewVertical -> {0, 0, 1}, ViewPoint -> {-100, 100, 75}]

chess board

YPieces =  Graphics3D[{EdgeForm[None], 
     ImportVox["~/OpenAssets/WorldChess/TXT/Y" <> # <> "Vox.txt"]}, 
    Boxed -> False, ImageSize -> 100,
    ViewVertical -> {0, 0, 1}, ViewPoint -> {-100, 100, 75}] & /@ ObjNames

Y Pieces

OPieces =  Graphics3D[{EdgeForm[None], 
     ImportVox["~/OpenAssets/WorldChess/TXT/O" <> # <> "Vox.txt"]}, 
    Boxed -> False, ImageSize -> 100, ViewVertical -> {0, 0, 1}, 
   ViewPoint -> {100, 100, 75}] & /@ ObjNames

O Pieces

Show[ GameBoard,
 YPieces[[1]] /.   Cuboid[{x_, y_, z_}] :> 
   Cuboid[{x + 5 + 8 #, y + 13, z + 2}] & /@ Range[0, 7],
 YPieces[[2]] /.   Cuboid[{x_, y_, z_}] :> 
     Cuboid[{x + 5 + 8 #, y + 5, z + 2}] & /@ {0, 7},
 YPieces[[3]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, x + 5, z + 2}] & /@ {1, 6},
 YPieces[[4]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, x + 5, z + 2}] & /@ {2, 5},
 YPieces[[5]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, x + 5, z + 2}] & /@ {3},
 YPieces[[6]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, x + 5, z + 2}] & /@ {4},

 OPieces[[1]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{x + 5 + 8 #, y + 13 + 8 5, z + 2}] & /@ Range[0, 7],
 OPieces[[2]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{x + 5 + 8 #, -y + 4 + 8 7, z + 2}] & /@ {0, 7},
 OPieces[[3]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, -x + 4 + 8 7, z + 2}] & /@ {1, 6},
 OPieces[[4]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, -x + 4 + 8 7, z + 2}] & /@ {2, 5},
 OPieces[[5]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, -x + 4 + 8 7, z + 2}] & /@ {3},
 OPieces[[6]] /. Cuboid[{x_, y_, z_}] :> 
     Cuboid[{y + 5 + 8 #, -x + 4 + 8 7, z + 2}] & /@ {4},
 ViewVertical -> {0, 0, 1}, ViewPoint -> {-100, 100, 75}]

Chess Game

Now if we had a rival player, we could turn this BB into a metaverse by posting and responding, move by move until checkmate is reached, but we might need a few more functions. And while we're at it, here's an article about chess on block chain.

POSTED BY: Brad Klee

Thank you for this presentation, Brad! Very well demonstrated.

Posted 3 years ago

The coloring is relatively easy to do, but it is tedious and takes time. In this case, we don't want extremist pieces, either all black or all white. We will take subsets out of each piece and color them differently.

BaseCol = {{_, 0 | 1, _} -> 0, {-1 | 0, 2, -2 | 1} -> 1, {-2 | 1, 2, -1 | 0} -> 1};

ColReps = {{}, {{_, 9 | 10, -3 | 2} -> 1, {-3 | 2, 9 | 10, _} -> 1},
   {{0, 9, -1 | 0} -> 1, {-2, 8 | 9 | 10, -1 | 0} ->  1 , 
    {-3, 9 | 8 | 7 | 6 | 5 | 4, -1 | 0} -> 1  },
   {{-1 | 0, 10 | 11 | 12, -1} -> 1   }, {{-1 | 0, 14 | 15, -1 | 0} -> 1  },
   {{-1 | 0, 11, -2 | 1} -> 1, {-2 | 1, 11, -1 | 0} -> 1}};

DefaultCol = {{_, _, _} -> 0};

g1 = Function[{col1, col2}, Show[MapThread[Graphics3D[
          Transpose[{#1 /. BaseCol /. #3 /. DefaultCol /. {0 -> col1, 
              1 -> col2},
            #1 /. {x_, y_, z_} :> Cuboid[{x , y, z + #2*8}]}]
          ] &, {AllVoxels, Range[6], ColReps}], ImageSize -> 700, 
       ViewVertical -> {0, 1, 0},
       ViewPoint -> {4, 1, -2}, Boxed -> False]] @@ # & /@ {{Yellow, 
     Orange}, {Orange, Yellow}};

Column[Show[#, ImageSize -> 500] & /@ g1]

Colored Chess Pieces

With a few lines of extra formatting, we can export these to a vox plaintext file

SixCols = MapThread[#1 /. BaseCol /. #3 /. DefaultCol &, 
    {AllVoxels, Range[6], ColReps}];
SixVerts = AllVoxels;
ObjNames = {"Pawn", "Rook", "Knight", "Bishop", "Queen", "King"};

VoxFileFormat[verts_, cols_, objname_] := With[{header = {
     StringJoin["Vox file for ", objname],
     "6x16x6 slab size ",
     "----------------",
     " x    y    z   c",
     "----------------"
     }},
  Join[header,
   MapThread[Append, {verts, cols}] /. {x_, y_, z_, c_} :> 
     StringJoin[
      If[Sign[x] == 1, " ", "-"],
      StringPadRight[ToString[Abs[x]], 4, " "],
      If[Sign[y] == 1, " ", "-"],
      StringPadRight[ToString[Abs[y]], 4, " "],
      If[Sign[z] == 1, " ", "-"],
      StringPadRight[ToString[Abs[z]], 4, " "],
      StringPadRight[ToString[c], 2, " "]
      ]]]

ObjFiles = MapThread[VoxFileFormat, {SixVerts, SixCols, ObjNames}];

MapThread[
  Export["~/OpenAssets/WorldChess/TXT/" <> #1 <> 
     "Vox.txt", #2] &, {ObjNames, ObjFiles}];

ChessPiecesData = 
  Map[ToExpression[DeleteCases[StringSplit[#, " "], ""]] &, 
     StringSplit[#, "\n"][[6 ;; -1]]] & /@ 
   Map[Import["~/OpenAssets/WorldChess/TXT/" <> #1 <> "Vox.txt"] &, 
    ObjNames];

Show[MapIndexed[
  Graphics3D[#1 /. {x_, y_, z_, 
       c_} :> {c /. {1 -> Yellow, 0 -> Orange},
       Cuboid[{x , y, z + #2[[1]] 8}]}] &, ChessPiecesData],
 ImageSize -> 700, ViewVertical -> {0, 1, 0}, ViewPoint -> {4, 1, -2},
  Boxed -> False]

Orange Pieces

The plaintext files are now up on github, but we still need a board. Next task...

POSTED BY: Brad Klee
Posted 3 years ago

enter image description here

The two advantages to using half distances are: 1. If smallest lattice distance is the same as edge length, then it is not easy to find a correct set of edges. 2. Voxel centers fall on sublattice between Voxel corners. So we can think about projecting to voxels as carving out a slab of stone:

Sculpting

How do we know to chisel away the point in red? Check separately on three dimensions to find where the point falls into the stack of facets

VoxelMemberQ[FacetList_, LatticePt_] := And @@ MapThread[
   EvenQ[Position[Sort[Append[#1, #2]], #2][[1, 1]]] &, {
    Function[{ind}, Union[Flatten[Select[FacetList[[All, 1]], 
          MemberQ[#, ReplacePart[LatticePt, ind -> _]] &][[All, All, 
          ind]]]]] /@ Range[3], LatticePt}]

Facets2Voxels[facets_] :=  With[{Slab = Flatten[Table[{i + 1/2, j + 1/2, k + 1/2},
      {i, -3, 2}, {j, 0, 16}, {k, -3, 2}], 2]},   Map[# - {1/2, 1/2, 1/2} &,
   Select[Slab, VoxelMemberQ[facets, # ] &]]]

AbsoluteTiming[AllVoxels = Facets2Voxels /@ AllFacets; ]

Show[MapIndexed[ Graphics3D[#1 /. {x_, y_, z_} :> Cuboid[{x , y, z + #2[[1]] 8}]
] &,  AllVoxels], ImageSize -> 700, ViewVertical -> {0, 1, 0}, 
ViewPoint -> {4, 1, -2},  Boxed -> False]
(*Slow?*)
Out[]={158,Null}

voxel pieces

And here's a cheap extra function for Pawn promotion:

SurfaceVoxels[voxels_] := Select[voxels, Length[
  Nearest[Complement[voxels, {#}], #]] != 6 &]

IterateTransorm[pFrom_, pTo_] :=  Union[Flatten[Nearest[Complement[pTo, {#}],
      #] & /@  Complement[pFrom, Complement[SurfaceVoxels[pFrom ], pTo]], 1]]

Pawn2Knight = NestList[IterateTransorm[#, AllVoxels[[3]] ] &, AllVoxels[[1]], 10];

Pawn2Queen = NestList[IterateTransorm[#, AllVoxels[[5]] ] &, AllVoxels[[1]], 10];

Pawn 2 Knight

Pawn 2 Queen

Most people these days want to see pawn to queen, but there are certain situations where pawn to knight is advantageous. Especially if the opponent king is well guarded, a jumper could be useful. Of course we can also color the voxels,

cols = Table[Blend[{Hue[RandomReal[{0, 1}]], Pink}], {Length@AllVoxels[[3]]}];

Graphics3D[Transpose[{cols, Cuboid /@ AllVoxels[[3]]}],
 ViewVertical -> {0, 1, 0}, ViewPoint -> {4, 2, -2},
 PlotRange -> {{-6, 6}, {-1, 16}, {-6, 6}}, Boxed -> False, 
 ImageSize -> 700]

disco knight

This coloring, while an improvement over the previous attempt, unfortunately, still looks too much like a Discothèque. I guess this would be fine for "Ready Player 1", but it is also irrespective of Western History where pieces are either Black or White. So we probably need to work a little more (later) to find a compromise how to color the pieces more appropriately.

Edit Nov. 26 An alternate, prob. better, animation for pawn to queen promotion follows:

Pawn to Queen

Suspicious that the animation repeats only twice. Thought that the code said $42$, but it's gone missing instead. Accidentally deleted?

POSTED BY: Brad Klee

Group Abstract Group Abstract