1
\$\begingroup\$

I have created a d20 dice (icosahedron) in xna and used BEPU physics to roll it. But now I'm trying to get the value from the dice after its rolled. I was thinking about looking for the 3 vertices that have the highest Y-axis coords after the dice rolled. so I could set something like this:

if (vertices[0].currentPosition.Y >= 2 && vertices[1].currentPosition.Y >= 2 && vertices[2].currentPosition.Y >= 2)
{
    diceValue = 5;
}

But how do i get the current position from the vertices? Or is there a better way to check what value is on top of the dice?

\$\endgroup\$

3 Answers 3

1
\$\begingroup\$

If you have the rotation matrix, I think the the easier is to get the cos between the normals and forget the vertex position.

The cos will be max and near 1 when the angle among the two vectors is near 0.

Each normal will have a face value so get the face value of the normal nearest to Vector3.UnitY is easy.

enter image description here

 Vector3 FaceNormals = new Vector3[] { Vector3.UnitX, -Vector3.UnitX, Vector3.UnitY, -Vector3.UnitY}
 int FaceValue = new int[] { 1,2,3,4 }

float cos = -1;
int faceValue = 0;
for (int n=0; n<FaceNormals.Length; n++) {
  var RotatedNormal = Vectro3.TransformNormal(FaceNormal[n], RotationMatrix);
  var newCos = Vector3.Dot(Vector3.UnitY , RotatedNormal );
  if (cos<newCos) {
     FaceValue = FaceValues[n];
     vod = newCos;
  }
}

where the Vector3.UnitY is fixed, and the face normal are rotated, so calculation is now in world space.

EDIT:

If you want to use vertices... calculate the center of each face, if the face has two triangles correlated:

var j =0;
Vector3[] Centers = new Vector3[indices.Lengt/6];
Vector3[] Normals= new Vector3[indices.Lengt/6];

for (int i =0; i<indices.lenght; i+=6)
{
      Vector3 center;
      for (int n=0; n<6;n++) {
          center+= vertices[indices[i+n]].Position;
      }
      center/=6;

      var A = vertices[indices[i+1]].Position - vertices[indices[i]].Position
      var B = vertices[indices[i+2]].Position - vertices[indices[i]].Position 
      normal = Vector3.Cross(A, B);

      Centers[j] = center;
      Normals[j] = normal;
      j++;
}

int[] FaceValues = { 1,2,3,4,5,6...}
float y = float.MinValue;
for (int n=0; n<Centers.Length; n++) {

    var rotated = Vector3.Transform(Centers[n], TransformMatrix);

    if (y < rotated.Y) {
        y = rotated.Y;
        FaceValue = FaceValues[n];
    }
}

You'll have the dice face value in "FaceValue"

\$\endgroup\$
11
  • \$\begingroup\$ For me the solution with the vertex position looks easier to understand, I'm still a bit new with xna and gamedevelopment ^^ And I also didn't set any normals yet so I cant use this solution yet I think. \$\endgroup\$ Commented Aug 31, 2014 at 16:00
  • \$\begingroup\$ It's not xna related problem, it's algebra ;) Each face has a normal, you can calculate it from three vertex as the cross product of (B-A)x(C-A), and you will need them when you want to apply some lighting to your model. This solution only calculates the Vector3.UnitY that is the vector of the face facing up in the rotated space, and later in that rotated space coords check agains every face normal, when the angle is 0 or near 0, it means that is the same direction and that is your face. \$\endgroup\$
    – Blau
    Commented Aug 31, 2014 at 16:12
  • \$\begingroup\$ if you want to use positions, you can transform your position with the rotation matrix, Vector3.Transform(vertexPos, rotationMatrix), but you will have to track what position owns to each face, and each face has at least three positions to check because different faces can share the same vertices, with normals each face is univocally identified \$\endgroup\$
    – Blau
    Commented Aug 31, 2014 at 16:16
  • \$\begingroup\$ Ok I see, so this should be the most efficient way then? \$\endgroup\$ Commented Aug 31, 2014 at 16:34
  • \$\begingroup\$ I still find it a bit hard to understand the code. I understand what it does but not how ^^ But I got it working with the vertexpositions so I got that going for me which is nice :p \$\endgroup\$ Commented Aug 31, 2014 at 17:35
1
\$\begingroup\$

Vertices are stored in memory using local coordinates. So no vertex has a "currentPosition," they're always positioned according to their offset from their original origin. In the "world," they are displayed according to some transformation: scale and rotation about their local origin, then translation with respect to the world origin. With the exception of static level geometry, vertices are almost never stored in a current position.

So, to get the "current" position of any vertex, you must apply a transformation to it that takes it from local to world space. Applied to all vertices, you need only find the vertices with the greatest Y coordinate.

It's been a while since I used XNA, but going off of the MSDN docs it should look something like this:

transform = Matrix.CreateFromQuaternion(dieRotation)
            * Matrix.CreateTranslation(diePosition);
var currentPositions = vertices
                       .Select(v => Vector3.Transform(v.Position, transform))
                       .OrderByDescending(p => p.Y)
                       .Take(numberOfVerticesPerFace)
                       .ToList();
\$\endgroup\$
8
  • \$\begingroup\$ how should i convert the var currentPositions to a list? I converted to string at the moment and i get this as result puu.sh/bdSuq/8354f13741.png \$\endgroup\$ Commented Aug 30, 2014 at 10:14
  • \$\begingroup\$ Just use ToList. I've added it to the example. While it probably doesn't matter in this case, you should avoid converting enumerations to list since that means allocating enough memory to hold it. Enumerations don't necessarily allocate arrays unless needed, such as in this case where there's a sort. \$\endgroup\$
    – jzx
    Commented Aug 31, 2014 at 2:51
  • \$\begingroup\$ Ok I'm starting to get some results :) I did it without converting to list like you recommended. "currentPositions.ElementAt(0).Y" But the result isn't really what I need, the enumeration only has the values from the vertices but now you don't now which vertex it is. puu.sh/bfspn/2e9569cb29.jpg So what should be the best way to know which the highest vertex really is? \$\endgroup\$ Commented Aug 31, 2014 at 14:18
  • \$\begingroup\$ As long as you as are you using linq on the list, you might as well replace the OrderByDescending and ToList with a Max(p => p.Y). Or you could do it in the select statement and really squash it down. \$\endgroup\$ Commented Aug 31, 2014 at 16:15
  • 1
    \$\begingroup\$ Faces share vertices so it's hard to calculate this way... If you want continue using vertices at least use only one per face, and it should be the center of the face to make them univocal. You know the center of a shape is the sum of the position divided by the numbre of vertices \$\endgroup\$
    – Blau
    Commented Sep 1, 2014 at 23:00
0
\$\begingroup\$

You can calculate the orientations that the die will fall on, then test the current orientation against a pre-computed list of orientations to get the number.

\$\endgroup\$
2
  • \$\begingroup\$ I already tested something like this, but it looked like the orientation wasnt always perfect the same for the same dice value for some reason (not a single axis was the same). But i gues I did something wrong there. Can you show some codesample from how it should be? But i think its easier to just check the highest vertices, because the code that jzx posted here seems pretty easy for me (if it works offcourse, did not have time to try it out yet) \$\endgroup\$ Commented Aug 29, 2014 at 22:15
  • \$\begingroup\$ You could try rounding the orientation of the die to a given decimal place, then comparing with the similarly rounded pre-computed orientations. This would give you a 'tolerance' in which it tests to be in a given orientation. I'd find the appropriate level of rounding by taking away decimal places until you got reliable, accurate results. \$\endgroup\$ Commented Aug 31, 2014 at 16:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .