Texture coordinates for triangle fans - algorithm

I am trying to "fill" a surface of some geometry that I drew. I am using GL_TRIANGLE_FAN primitive. (for example : 1 hub (center) point and 12 other points). I have calculated texture coordinates for each vertex in the interval 0-1. But as a result I get this , its a little bit confused. I desire to get result like that image . Please help, what is wrong here?
How can I calculate correct texture coordinates in such Triangulation ( GL_TRIANGLE_FAN )
in the image red dots is my points
*Code - Snippet :
assert(("CROSS type intersection needs only 5 vertices : center point and "
"rest points in anticlockwise order", (lp->size() > 5) && (lp->size() < 5)));
osg::Vec3 vAlong_1,vAlong_2;
vAlong_1 = (*lp)[1] - (*lp)[4];
vAlong_2 = (*lp)[1] - (*lp)[2];
eps = ((*lp)[2] - (*lp)[4]).length() * 0.2 / 2;
vAlong_1.normalize();
vAlong_2.normalize();
_edgeCoords->push_back((*lp)[0]);
_edgeCoords->push_back((*lp)[1]);
if (CMF::euclidDistance((*lp)[0],(*lp)[1]) <= CMF::euclidDistance((*lp)[0],(*lp)[2])) {
float cosAlpha = -(vAlong_1 * vAlong_2);
float extraLength = ((*lp)[2] - (*lp)[1]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[1] + vAlong_1 * (eps + extraLength));
_edgeCoords->push_back((*lp)[2] + vAlong_1 * eps);
} else {
float cosAlpha = (vAlong_1 * vAlong_2);
float extraLength = ((*lp)[2] - (*lp)[1]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[1] + vAlong_1 * eps);
_edgeCoords->push_back((*lp)[2] + vAlong_1 * (eps + extraLength));
}
_edgeCoords->push_back((*lp)[2]);
if (CMF::euclidDistance((*lp)[0],(*lp)[2]) <= CMF::euclidDistance((*lp)[0],(*lp)[3])) {
float cosAlpha = -(vAlong_1 * vAlong_2);
float extraLength = ((*lp)[3] - (*lp)[2]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[2] - vAlong_2 * (eps + extraLength));
_edgeCoords->push_back((*lp)[3] - vAlong_2 * eps);
} else {
float cosAlpha = (vAlong_1 * vAlong_2);
float extraLength = ((*lp)[3] - (*lp)[2]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[2] - vAlong_2 * eps);
_edgeCoords->push_back((*lp)[3] - vAlong_2 * (eps + extraLength));
}
_edgeCoords->push_back((*lp)[3]);
if (CMF::euclidDistance((*lp)[0],(*lp)[3]) <= CMF::euclidDistance((*lp)[0],(*lp)[4])) {
float cosAlpha = -(vAlong_1 * vAlong_2);
float extraLength = ((*lp)[4] - (*lp)[3]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[3] - vAlong_1 * (eps + extraLength));
_edgeCoords->push_back((*lp)[4] - vAlong_1 * eps);
} else {
float cosAlpha = (vAlong_1 * vAlong_2);
float extraLength = ((*lp)[4] - (*lp)[3]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[3] - vAlong_1 * eps);
_edgeCoords->push_back((*lp)[4] - vAlong_1 * (eps + extraLength));
}
_edgeCoords->push_back((*lp)[4]);
if (CMF::euclidDistance((*lp)[0],(*lp)[1]) <= CMF::euclidDistance((*lp)[0],(*lp)[4])) {
float cosAlpha = -(vAlong_1 * vAlong_2);
float extraLength = ((*lp)[4] - (*lp)[1]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[4] + vAlong_2 * eps);
_edgeCoords->push_back((*lp)[1] + vAlong_2 * (eps + extraLength));
} else {
float cosAlpha = (vAlong_1 * vAlong_2);
float extraLength = ((*lp)[4] - (*lp)[1]).length() * cosAlpha;
_edgeCoords->push_back((*lp)[4] + vAlong_2 * (eps + extraLength));
_edgeCoords->push_back((*lp)[1] + vAlong_2 * eps);
}
_edgeCoords->push_back((*lp)[1]);
_tCoords->push_back(osg::Vec2(0.5,0.5));
_tCoords->push_back(osg::Vec2(0.666,0.666));
_tCoords->push_back(osg::Vec2(0.666,1.0));
_tCoords->push_back(osg::Vec2(0.333,1.0));
_tCoords->push_back(osg::Vec2(0.333,0.666));
_tCoords->push_back(osg::Vec2(0.0,0.666));
_tCoords->push_back(osg::Vec2(0.0,0.333));
_tCoords->push_back(osg::Vec2(0.333,0.333));
_tCoords->push_back(osg::Vec2(0.333,0.0));
_tCoords->push_back(osg::Vec2(0.666,0.0));
_tCoords->push_back(osg::Vec2(0.666,0.333));
_tCoords->push_back(osg::Vec2(1.0,0.333));
_tCoords->push_back(osg::Vec2(1.0,0.666));
_tCoords->push_back(osg::Vec2(0.666,0.666));

Try keeping the 2d positions always equal to the texture coordinates for each vertex. That will ensure your geometry appears as an undistorted cutout of your texture. You can then rescale and center the mesh as you like without distorting the texture by applying transforms to the vertex positions.
One way to do this would be to create a function that pushes a single vertex, accepting the 2d coordinates of the vertex and any transforms you want to apply. The function would then push the 2d coordinates as texcoords, then transform them and push the result as positions.

Related

Why is matrix multiplication row x row 4-5 times slower than row x column on Mali's GPU?

Recently, I encountered a problem when using computer shader to develop matrix multiplication. A common matrix multiplication C = AB. in order to make the memory continuous, I transposed the B matrix. I think this can speed up the running speed. However, when I measured the speed, I found that the form of line X was several times slower than that of line X. I explored it for a long time and couldn't understand it, so I wrote down the problem for help!!!
My environment Mali G77 (MediaTek Dimensity 1200)
A matrix dimension: 4x2048x2048
B matrix dimension: 4x2048x2048
Time comparison:
Row x row: About 9s
Row x column: about 1.6s
Column x column: about 3.3s
question demo:https://github.com/yikox/ProfilerDemo
shader code:
//computer shader
#version 310 es
#define XLOCAL 8
#define YLOCAL 8
#define ZLOCAL 1
layout(binding = 0) writeonly buffer soutput{
vec4 data[];
} uOutput;
layout(binding = 1) readonly buffer sinput0{
vec4 data[];
} uInput0;
layout(binding = 2) readonly buffer sinput1{
vec4 data[];
} uInput1;
layout(location=3) uniform ivec4 uInputSize0;
layout(location=4) uniform ivec4 uInputSize1;
layout(location=5) uniform ivec4 uOutputSize;
layout (local_size_x = XLOCAL, local_size_y = YLOCAL, local_size_z = ZLOCAL) in;
//矩阵A和矩阵B相乘的某一列的第I个元素
vec4 PixelMul(int i, ivec3 pos)
{
// 行x行
// vec4 data0 = uInput0.data[i + pos.y * uInputSize0.x + pos.z * uInputSize0.x * uInputSize0.y];
// vec4 data1 = uInput1.data[i + pos.x * uInputSize1.y + pos.z * uInputSize1.x * uInputSize1.y];
// 行x列
// vec4 data0 = uInput0.data[i + pos.y * uInputSize0.x + pos.z * uInputSize0.x * uInputSize0.y];
// vec4 data1 = uInput1.data[pos.x + i * uInputSize1.y + pos.z * uInputSize1.x * uInputSize1.y];
// 列x列
vec4 data0 = uInput0.data[pos.y + i * uInputSize0.x + pos.z * uInputSize0.x * uInputSize0.y];
vec4 data1 = uInput1.data[pos.x + i * uInputSize1.y + pos.z * uInputSize1.x * uInputSize1.y];
return data0 * data1;
}
void main()
{
ivec3 pos = ivec3(gl_GlobalInvocationID) * ivec3(2, 2, 1);
if(all(lessThan(pos, uOutputSize.xyz)))
{
vec4 outData00 = vec4(0);
vec4 outData01 = vec4(0);
vec4 outData10 = vec4(0);
vec4 outData11 = vec4(0);
for(int i = 0; i < uInputSize0.x; i++)
{
outData00 += PixelMul(i, pos + ivec3(0, 0, 0));
outData01 += PixelMul(i, pos + ivec3(1, 0, 0));
outData10 += PixelMul(i, pos + ivec3(0, 1, 0));
outData11 += PixelMul(i, pos + ivec3(1, 1, 0));
}
uOutput.data[pos.x + 0 + (pos.y + 0) * uOutputSize.x + pos.z * uOutputSize.x * uOutputSize.y] = outData00;
uOutput.data[pos.x + 1 + (pos.y + 0) * uOutputSize.x + pos.z * uOutputSize.x * uOutputSize.y] = outData01;
uOutput.data[pos.x + 0 + (pos.y + 1) * uOutputSize.x + pos.z * uOutputSize.x * uOutputSize.y] = outData10;
uOutput.data[pos.x + 1 + (pos.y + 1) * uOutputSize.x + pos.z * uOutputSize.x * uOutputSize.y] = outData11;
}
}

How can I calculate the inertia tensor of a hollow object defined by a triangle mesh?

I want to calculate the mass, center of mass, and inertia tensor of player created objects. Some of the objects will be hollow instead of solid. I am creating a closed triangle mesh for their object. Note that this is for a physics building game so I want the values to be as accurate as possible (within reason).
Based on the references listed below, I can get the properties for a solid object by creating a tetrahedron from each triangle of the mesh and calculating the signed volume which gives the mass (volume * density) and the center of mass. I even got the inertia tensor calculation working.
How do I do the same for a hollow object?
For the mass and center of mass, I am iterating through the triangles of the mesh and totaling their area and calculating the area-weighted average of their positions, then multiplying the surface area by a "density" value to get the mass.
public static (float, Vector3) CalculateSurfaceArea(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
float totalArea = 0f;
Vector3 temp = Vector3.zero;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 edge21 = v2 - v1;
Vector3 edge31 = v3 - v1;
float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
totalArea += area;
Vector3 triCenter = (v1 + v2 + v3) / 3f; // center of the triangle
temp += triCenter * area;
}
Vector3 areaCenter = temp / totalArea;
return (totalArea, areaCenter);
}
For the inertia tensor, I am trying a similar approach where I iterate through all the triangles and total up their moments of inertia and using the parallel-axis theorem to account for their positions but (a) I am not sure this is correct and (b) how do I calculate the products of inertia (Ixy, Ixz, Iyz)?
public static (Vector3, Quaternion) CalculateHollowInertiaTensor(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
double Ixx = 0f;
double Iyy = 0f;
double Izz = 0f;
double Ixy = 0f;
double Ixz = 0f;
double Iyz = 0f;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 edge21 = v2 - v1;
Vector3 edge31 = v3 - v1;
Vector3 center = (v1 + v2 + v3) / 3f; // center of the triangle
Vector3 offset = center - v1;
float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
// Moment of inertia of triangle rotating around the first vertex
// https://en.wikipedia.org/wiki/List_of_moments_of_inertia
// I = (1/6)m(P.P + P.Q + Q.Q)
float triIxx = (edge21.y * edge21.y + edge21.z * edge21.z + edge21.y * edge31.y + edge21.z * edge31.z + edge31.y * edge31.y + edge31.z * edge31.z) / 6f;
float triIyy = (edge21.x * edge21.x + edge21.z * edge21.z + edge21.x * edge31.x + edge21.z * edge31.z + edge31.x * edge31.x + edge31.z * edge31.z) / 6f;
float triIzz = (edge21.x * edge21.x + edge21.y * edge21.y + edge21.x * edge31.x + edge21.y * edge31.y + edge31.x * edge31.x + edge31.y * edge31.y) / 6f;
// Shift to the center of the triangle
triIxx -= offset.y * offset.y + offset.z * offset.z;
triIyy -= offset.x * offset.x + offset.z * offset.z;
triIzz -= offset.x * offset.x + offset.y * offset.y;
// Shift to the origin (using parallel-axis theorem)
triIxx += center.y * center.y + center.z * center.z;
triIyy += center.x * center.x + center.z * center.z;
triIzz += center.x * center.x + center.y * center.y;
Ixx += triIxx * area;
Iyy += triIyy * area;
Izz += triIzz * area;
//Ixy += area * center.x * center.y;
//Ixz += area * center.x * center.z;
//Iyz += area * center.y * center.z;
}
Matrix<double> inertiaTensor = Matrix<double>.Build.Dense(3, 3);
inertiaTensor[0, 0] = Ixx;
inertiaTensor[1, 1] = Iyy;
inertiaTensor[2, 2] = Izz;
inertiaTensor[0, 1] = inertiaTensor[1, 0] = -Ixy;
inertiaTensor[0, 2] = inertiaTensor[2, 0] = -Ixz;
inertiaTensor[1, 2] = inertiaTensor[2, 1] = -Iyz;
Debug.Log(inertiaTensor);
//Matrix<double> inertiaTensorInverse = inertiaTensor.Inverse();
// Find the principal axes and simplified inertia tensor
Evd<double> evd = inertiaTensor.Evd();
Vector3 inertiaTensorDiagonal = MakeVector3(evd.EigenValues.Real());
Quaternion inertiaTensorRotation = Quaternion.LookRotation(
MakeVector3(evd.EigenVectors.Column(2)),
MakeVector3(evd.EigenVectors.Column(1))
);
return (inertiaTensorDiagonal, inertiaTensorRotation);
}
References I have found so far:
How to calculate the volume of a 3D mesh object the surface of which is made up triangles
How to calculate the volume of a 3D mesh object the surface of which is made up triangles
List of moments of inertia
https://en.wikipedia.org/wiki/List_of_moments_of_inertia
Polyhedral Mass Properties, David Eberly
https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf
Explicit Exact Formulas for the 3-D Tetrahedron Inertia Tensor in Terms of its Vertex Coordinates, F. Tonon
http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf
Efficient Feature Extraction for 2D/3D Objects in Mesh Representation, Cha Zhang and Tsuhan Chen
http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf
===== EDIT =====
Based on John Alexiou's answer, I reimplemented the algorithm for hollow objects:
public static (float, Vector3, Vector3, Quaternion) CalculateHollowMassCenterInertiaTensor(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
double Ixx = 0f;
double Iyy = 0f;
double Izz = 0f;
double Ixy = 0f;
double Ixz = 0f;
double Iyz = 0f;
float totalMass = 0f;
Vector3 temp = Vector3.zero;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 center = (v1 + v2 + v3) / 3f; // center of the triangle
float area = (Vector3.Cross(v1, v2) + Vector3.Cross(v2, v3) + Vector3.Cross(v3, v1)).magnitude / 2f; // area of the triangle
totalMass += area;
temp += center * area;
Ixx += area * (v1.y * v1.y + v1.z * v1.z + v2.y * v2.y + v2.z * v2.z + v3.y * v3.y + v3.z * v3.z) / 3f;
Iyy += area * (v1.x * v1.x + v1.z * v1.z + v2.x * v2.x + v2.z * v2.z + v3.x * v3.x + v3.z * v3.z) / 3f;
Izz += area * (v1.x * v1.x + v1.y * v1.y + v2.x * v2.x + v2.y * v2.y + v3.x * v3.x + v3.y * v3.y) / 3f;
Ixy += area * (v1.x * v1.y + v2.x * v2.y + v3.x * v3.y) / 3f;
Ixz += area * (v1.x * v1.z + v2.x * v2.z + v3.x * v3.z) / 3f;
Iyz += area * (v1.y * v1.z + v2.y * v2.z + v3.y * v3.z) / 3f;
}
Vector3 centerOfMass = temp / totalMass;
Matrix<double> inertiaTensor = Matrix<double>.Build.Dense(3, 3);
inertiaTensor[0, 0] = Ixx;
inertiaTensor[1, 1] = Iyy;
inertiaTensor[2, 2] = Izz;
inertiaTensor[0, 1] = inertiaTensor[1, 0] = -Ixy;
inertiaTensor[0, 2] = inertiaTensor[2, 0] = -Ixz;
inertiaTensor[1, 2] = inertiaTensor[2, 1] = -Iyz;
Debug.Log(inertiaTensor);
// Find the principal axes and simplified inertia tensor
Evd<double> evd = inertiaTensor.Evd();
Vector3 inertiaTensorDiagonal = MakeVector3(evd.EigenValues.Real());
Quaternion inertiaTensorRotation = Quaternion.LookRotation(
MakeVector3(evd.EigenVectors.Column(2)),
MakeVector3(evd.EigenVectors.Column(1))
);
return (totalMass, centerOfMass, inertiaTensorDiagonal, inertiaTensorRotation);
}
It is giving me the wrong values for the inertia tensor though.
mass
Expected Inertial Tensor
Actual Inertial Tensor
Cube
6.00
1.6667 1.6667 1.6667
3.0000 3.0000 3.0000
Sphere
3.11
0.5236 0.5236 0.5236
0.5151 0.5162 0.5163
Cylinder
7.80
4.5488 1.7671 4.5488
8.7134 1.8217 8.7134
Cube has 1m sides, Sphere has 0.5m radius, Cylinder has 0.5m radius and 2m height. All are centered around the origin.
Note that I am using the total surface area as the mass. Assume that the thickness * density = 1 so mass = area * thickness * density becomes mass = area * 1. I will be picking better values for those later because I assume that will not have that big of an effect on the algorithm.
Also note that some rounding is to be expected because the shapes are being approximated by a triangular mesh.
For a shell body to have mass and mass moment of inertia, the sides must have some thickness ε>0.
This defines the mass of a triangle defined by the vectors A, B and C and thickness ε and density ρ as
area = 1/2*Math.Abs( Vector3.Cross(A,B) + Vector3.Cross(B,C) + Vector3.Cross(C,A) )
mass = ρ*area*ε
or the above can be used to find the density from the total mass of an object, once the total surface area is calculated.
To find the mass moment of inertia you need to define a function returning the MMOI matrix of a unit mass particle from the location vector r=(x,y,z).
Matrix3 Mmoi(Vector3 r)
{
// | y^2+z^2 -x y -x z |
// I = | -x y x^2+z^2 -y z |
// | -x z -y z x^2+y^2 |
return new Matrix3(
r.y*r.y + r.z*r.z, -r.x*r.y, -r.x*r.y,
-r.x*r.y, r.x*r.x + r.z*r.y, -r.y*r.z,
-r.x*r.z, -r.y*r.z, r.x*r.x + r.y*r.y);
}
and then calculate the MMOI of a triangle from the vertices and the mass
Matrix3 Mmoi(double m, Vector3 A, Vector3 B, Vector3 C)
{
return (m/3)*(Mmoi(A)+Mmoi(B)+Mmoi(C));
}
The above is derived from the surface integral over the triangle, and since [SO] does not support math formatting I am omitting the details here.
Yes the above is true, the MMOI of a surface triangle is that of the average MMOI of the three vertices.
Update
In correlating the above with CAD I realized you have to integrate over both front and back surfaces of the triangle. The result that matches CAD mass properties is
Matrix3 Mmoi(double m, Vector3 A, Vector3 B, Vector3 C)
{
return 2*(m/3)*(Mmoi(A)+Mmoi(B)+Mmoi(C));
}

How does this 2d noise generation function work? Does it have a name?

I came across this 2D noise function in the Book of Shaders
float noise(vec2 st) {
vec2 integerPart = floor(st);
vec2 fractionalPart = fract(st);
float s00 = random(integerPart);
float s01 = random(integerPart + vec2(0.0, 1.0));
float s10 = random(integerPart + vec2(1.0, 0.0));
float s11 = random(integerPart + vec2(1.0, 1.0));
float dx1 = s10 - s00;
float dx2 = s11 - s01;
float dy1 = s01 - s00;
float dy2 = s11 - s10;
float alpha = smoothstep(0.0, 1.0, fractionalPart.x);
float beta = smoothstep(0.0, 1.0, fractionalPart.y);
return s00 + alpha * dx1 + (1 - alpha) * beta * dy1 + alpha * beta * dy2;
}
It is clear what this function does: it generates four random numbers at the vertices of a square, then interpolates them. What I am finding difficult is understanding why the interpolation (the s00 + alpha * dx1 + (1 - alpha) * beta * dy1 + alpha * beta * dy2 expression) works. How is it interpolating the four values when it does not seem to be symmetric in the x and y values?
If you expand the last line, it's:
return s00 * (1-alpha) * (1-beta) +
s10 * alpha * (1-beta) +
s01 * (1-alpha) * beta +
s11 * alpha * beta;
Which is symmetric in x and y. If you add up the weights:
alpha * beta + (1-alpha) * beta + alpha * (1-beta) + (1-alpha) * (1-beta)
= (alpha + 1-alpha) * beta + (alpha + 1-alpha) * (1-beta)
= beta + 1-beta
= 1
so it's an affine combination of the values at the corners

Severe artifact when interpolating between dual quaternions

I'm having trouble with my implementation of dual quaternion skinning. I'm still learning about the subject, so for the moment I'm converting from the bone matrix to a dual quaternion CPU side, and back to a matrix in the shader.
The conversion does apparently work correctly for single bones, but if I try to linearly blend between dual quaternions, I get this artifact:
http://imagizer.imageshack.us/a/img838/8671/nun.gif
I don't know what's causing this. Maybe it's related to how I normalize the dual quaternion, maybe it's in how I convert from dual quat to matrix. I've tried searching for actual dual quaternion code, but all I find is a bunch of hard to read mathematical definitions.
I am including pieces of the shader code, as I'm pretty sure that's where the problem is. Hopefully somebody proficient in quaternion math can look through it!
Blending the dual quaternions. Boneweight2 = (1.0 - boneweight1), so they'll always sum up to 1.
vec4 blendReal = boneReal[bone1] * boneWeight1 + boneReal[bone2] * boneWeight2;
vec4 blendDual = boneDual[bone1] * boneWeight1 + boneDual[bone2] * boneWeight2;
float blend_norm_real = length(blendReal);
blendReal /= blend_norm_real;
blendDual /= blend_norm_real;
Create matrix from dual quaternion:
mat4 MatFromDualQuat(vec4 rq, vec4 dq)
{
//Source: Section 3.4 http://www.seas.upenn.edu/~ladislav/papers/sdq-i3d07/sdq-i3d07.pdf
//rq = real quaternion
//dq = dual quaternion
mat4 M;
M[0][0] = 1.0 - 2.0 * (rq.y * rq.y + rq.z * rq.z); //
M[1][0] = 2.0 * (rq.x * rq.y + rq.w * rq.z);//
M[2][0] = 2.0 * (rq.w * rq.y - rq.x * rq.z);//
M[3][0] = 0.0;
M[0][1] = 2.0 * (rq.x * rq.y - rq.w * rq.z);
M[1][1] = 1.0 - 2.0 * (rq.x * rq.x + rq.z * rq.z);
M[2][1] = 2.0 * (rq.y * rq.z + rq.w * rq.x);
M[3][1] = 0.0;
M[0][2] = - 2.0 * (rq.x * rq.z + rq.w * rq.y);
M[1][2] = 2.0 * (rq.y * rq.z - rq.w * rq.x);
M[2][2] = 1.0 - 2.0 * (rq.x * rq.x + rq.y * rq.y);
M[3][2] = 0.0;
M[0][3] = 2.0 * (-dq.w * rq.x + dq.x * rq.w + dq.z * rq.y - dq.y * rq.z);
M[1][3] = 2.0 * (-dq.w * rq.y + dq.y * rq.w + dq.x * rq.z - dq.z * rq.x);
M[2][3] = 2.0 * (-dq.w * rq.z + dq.z * rq.w + dq.y * rq.x - dq.x * rq.y);
M[3][3] = 1.0;
return M;
}
And then I multiply that with the bind pose vertex position.

Tinting a terrain model in a radius around a given point in XNA 4.0

I'm writing a game in Visual Studio 2010, using the XNA 4.0 framework. I have a 3D terrain model generated from a height map. What I'm trying to accomplish is to tint this model in a given radius around a certain point, the end goal being to display to the player the radius in which a unit can move in a given turn. The method I'm using to draw the model at the moment is this:
void DrawModel(Model model, Matrix worldMatrix)
{
Matrix[] boneTransforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(boneTransforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index] * worldMatrix;
effect.View = camera.viewMatrix;
effect.Projection = camera.projectionMatrix;
effect.EnableDefaultLighting();
effect.EmissiveColor = Color.Green.ToVector3();
effect.PreferPerPixelLighting = true;
// Set the fog to match the black background color
effect.FogEnabled = true;
effect.FogColor = Color.CornflowerBlue.ToVector3();
effect.FogStart = 1000;
effect.FogEnd = 3200;
}
mesh.Draw();
}
}
Also, in case it's relevant, I followed this tutorial http://create.msdn.com/en-US/education/catalog/sample/collision_3d_heightmap to create my heightmap and terrain.
Thanks in advance for any help!
You can use a shader to achieve that...
you only would need to pass as argument the world position of the center and the radius,
and let the pixel shader receive the pixel world position interpolated from the vertex shader as a texture coord...
then only have to check the distance of the pixel position to the center and tint it with a color if the pixel position is in range...
The technique you are looking for is called decaling.
You have to extract the part of the terrain, where the circle will be drawn, apply an appropriate texture to that part and draw it blending it with the terrain.
For the case of a terrain based on a uniform grid, this will look like the following:
You have the center position of the decal and its radius. Then you can determine min and max row/col in the grid, so that the cells include every drawn region. Create a new vertex buffer from these vertices. Positions can be read from the heightmap. You have to alter the texture coordinates, so the texture will be placed at the right position. Assume, the center position has coordinate (0.5, 0.5), center position + (radius, radius) has coordinate (1, 1) and so on. With that you should be able to find an equation for the texture coordinates for each vertex.
In the above example, the top left red vertex has texture coordinates of about (-0.12, -0.05)
Then you have the subgrid of the terrain. Apply the decal texture to it. Set an appropriate depth bias (you have to try out some values). In most cases, a negative SlopeScaleDepthBias will work. Turn off texture coordinate wrapping in the sampler. Draw the subgrid.
Here's some VB SlimDX Code I wrote for that purpose:
Public Sub Init()
Verts = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2) ^ 2
Tris = (Math.Ceiling(2 * Radius / TriAngleWidth) + 1) ^ 2 * 2
Dim Indices(Tris * 3 - 1) As Integer
Dim curN As Integer
Dim w As Integer
w = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2)
For y As Integer = 0 To w - 2
For x As Integer = 0 To w - 2
Indices(curN) = x + y * w : curN += 1
Indices(curN) = x + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + (y) * w : curN += 1
Indices(curN) = x + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + y * w : curN += 1
Next
Next
VB = New Buffer(D3DDevice, New BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes))
IB = New Buffer(D3DDevice, New DataStream(Indices, False, False), New BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4))
End Sub
Public Sub Update()
Dim Vertex(Verts - 1) As VertexPosTexColor.Struct
Dim curN As Integer
Dim rad As Single 'The decal radius
Dim height As Single
Dim p As Vector2
Dim yx, yz As Integer
Dim t As Vector2 'texture coordinates
Dim center As Vector2 'decal center
For y As Integer = Math.Floor((center.Y - rad) / TriAngleWidth) To Math.Floor((center.Y - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1
For x As Integer = Math.Floor((center.X - rad) / TriAngleWidth) To Math.Floor((center.X - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1
p.X = x * TriAngleWidth
p.Y = y * TriAngleWidth
yx = x : yz = y
If yx < 0 Then yx = 0
If yx > HeightMap.GetUpperBound(0) Then yx = HeightMap.GetUpperBound(0)
If yz < 0 Then yz = 0
If yz > HeightMap.GetUpperBound(1) Then yz = HeightMap.GetUpperBound(1)
height = HeightMap(yx, yz)
t.X = (p.X - center.X) / (2 * rad) + 0.5
t.Y = (p.Y - center.Y) / (2 * rad) + 0.5
Vertex(curN) = New VertexPosTexColor.Struct With {.Position = New Vector3(p.X, hoehe, p.Y), .TexCoord = t, .Color = New Color4(1, 1, 1, 1)} : curN += 1
Next
Next
Dim data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None)
data.Data.WriteRange(Vertex)
D3DContext.UnmapSubresource(VB, 0)
End Sub
And here's the according C# code.
public void Init()
{
Verts = Math.Pow(Math.Ceiling(2 * Radius / TriAngleWidth) + 2, 2);
Tris = Math.Pow(Math.Ceiling(2 * Radius / TriAngleWidth) + 1, 2) * 2;
int[] Indices = new int[Tris * 3];
int curN;
int w;
w = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2);
for(int y = 0; y <= w - 2; ++y)
{
for(int x = 0; x <= w - 2; ++x)
{
Indices[curN] = x + y * w ; curN += 1;
Indices[curN] = x + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + (y) * w ; curN += 1;
Indices[curN] = x + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + y * w ; curN += 1;
}
}
VB = new Buffer(D3DDevice, new BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes));
IB = new Buffer(D3DDevice, new DataStream(Indices, False, False), new BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4));
}
public void Update()
{
VertexPosTexColor.Struct[] Vertex = new VertexPosTexColor.Struct[Verts] ;
int curN;
float rad; //The decal radius
float height;
Vector2 p;
int yx, yz;
Vector2 t; //texture coordinates
Vector2 center; //decal center
for(int y = Math.Floor((center.Y - rad) / TriAngleWidth); y <= Math.Floor((center.Y - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1; ++y)
for(int x = Math.Floor((center.X - rad) / TriAngleWidth); x <= Math.Floor((center.X - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1; ++x)
{
p.X = x * TriAngleWidth;
p.Y = y * TriAngleWidth;
yx = x ; yz = y;
if( yx < 0)
yx = 0;
if (yx > HeightMap.GetUpperBound(0))
yx = HeightMap.GetUpperBound(0);
if (yz < 0)
yz = 0;
if (yz > HeightMap.GetUpperBound(1))
yz = HeightMap.GetUpperBound(1);
height = HeightMap[yx, yz];
t.X = (p.X - center.X) / (2 * rad) + 0.5;
t.Y = (p.Y - center.Y) / (2 * rad) + 0.5;
Vertex[curN] = new VertexPosTexColor.Struct() {Position = new Vector3(p.X, hoehe, p.Y), TexCoord = t, Color = New Color4(1, 1, 1, 1)}; curN += 1;
}
}
var data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None);
data.Data.WriteRange(Vertex);
D3DContext.UnmapSubresource(VB, 0);
}

Resources