I want to create a page roll effect in a shader. So i have a XZ plane points with y=0. Now i assume a cylender with R radius and Inf. height is lied down on the plane with certain angle rotated in Y axis. See the image:
I want a equation so that paper can rolled over the sphere in the given XZ direction.
what I am doing is:
float2 currPoint = gl_Vertex.xz;
float2 normDir = normalize(-1, 0); //direction at which paper will start rolling out.
float cylRadius = 1.f;
float dist = sqrt(normDir.x *vi.x * vi.x + normDir.y *vi.y * vi.y);
float beta = dist / cylRadius;
float3 outPos = 0;
outPos.x = currPoint.x + N.x * cylRadius * sin(beta);
outPos.z = cylRadius * (1 -cos(beta));
outPos.y = currPoint.y + N.y * cylRadius * sin(beta);
but it only works in the case of normDir = normalize(-1, 0), in other cases result not as expected.
I got this.My implementation is based on Pawel's page Flip implimentation ( http://nomtek.com/page-flip-3d/ )
Here is the code in HLSL.
float DistToLine(float2 pt1, float2 pt2, float2 testPt)
{
float2 lineDir = pt2 - pt1;
float2 perpDir = float2(lineDir.y, -lineDir.x);
float2 dirToPt1 = pt1 - testPt;
return (dot(normalize(perpDir), dirToPt1));
}
float3 Roll(float2 pos ) //per vertex
{
float time = param1.z ;
float t = (time);
float2 A = float2( 0 , 1 ); //tweak these 4 variables for the direction of Roll
float2 B = float2( 5.f , 1 ); //
float2 C = float2( 1 , 0 ); //
float2 D = float2( 0 , 0 ); //
float2 P1 = lerp( B , A , time ) ;
float2 P2 = lerp( C , D , time ) ; ;
float2 N = normalize( float2(-(P2-P1).y , (P2-P1).x ) );
float dist = DistToLine(P1 , P2 , float2(pos.x , pos.y) );
float3 vOut;
if (dist > 0 )
{
float distFromEnd = DistToLine(C , B , float2(pos.x , pos.y) ) ;
float R = lerp( .1 , .13 , distFromEnd );
float2 p = pos - N * dist;
float alpha = dist / R;
float sinAlpha = R * sin(alpha);
vOut.x = p.x + N.x * sinAlpha;
vOut.y = p.y + N.y * sinAlpha;
vOut.z = (1 - cos(alpha)) * R;
}
else
{
vOut.x = pos.x;
vOut.y = pos.y;
vOut.z = 0;
}
return vOut;
}
Related
I am able to bevel the corner of a rectangle.
When i strech the rectangle and than try to bevel it then the result does not look smooth , it should look like the rectangle on the right side.
How do i calculate the points for the trianlgle fan when the rectangle is streched ?
currently this is the way i am calculating the points for the Quarter circle.
std::vector<float> bevelData;
bevelData.push_back(0.0); // First set the centre of the rectangle to the data
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
for (int i = 0; i <= segments; ++i) {
float x, y;
float angle = start_angle + 0.5 * M_PI * i / static_cast<float>(segments);
x = circX + cos(angle) * rad; // circX is the centre of the circle as marked in yellow in the first image
y = circY + sin(angle) * rad; // circY is the centre of the circle as marked in yellow in the first image , rad is the radius of the circle
bevelData.push_back(x);
bevelData.push_back(y);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
}
After applying soultion this is the result i get.
//Bevel Bottom Right
float rightWidthBottom = (width / 2) - rightBottomBevel;
float rightHeightBottom = (height / 2) - rightBottomBevel;
std::vector<float> bottomRightBevelData = draw_bevel(rightWidthBottom, rightHeightBottom, rightBottomBevel, 1, -1, iSegmentsRightBottom);
std::vector<float> SuperRectangle::draw_bevel(float p_x, float p_y, float rad, int dir_x, int dir_y , int segments)
{
std::vector<float> bevelData;
float c_x, c_y; // the center of the circle
float start_angle; // the angle where to start the arc
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
c_x = p_x * dir_x;
c_y = p_y * dir_y;
if (dir_x == 1 && dir_y == 1)
start_angle = 0.0;
else if (dir_x == 1 && dir_y == -1)
start_angle = -M_PI * 0.5f;
else if (dir_x == -1 && dir_y == 1)
start_angle = M_PI * 0.5f;
else if (dir_x == -1 && dir_y == -1)
start_angle = M_PI;
for (int i = 0; i <= segments; ++i) {
float x, y;
float angle = start_angle + 0.5 * M_PI * i / static_cast<float>(segments);
x = c_x + cos(angle) * rad;
y = c_y + sin(angle) * rad;
float fscale = (y / (float)(height / 2.0f));
x = (x + (strech * fscale));
bevelData.push_back(x);
bevelData.push_back(y);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
}
return bevelData;
}
//////////////////////////////////////////////////////////////////
float xWidth = width / 2;
float yHeight = height / 2;
float TriangleRight[] = {
// positions // Normals // Texture Coord
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth + strech , yHeight - rightTopBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
xWidth - strech , -yHeight + rightBottomBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleLeft[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
-xWidth + strech , yHeight - leftTopBevel ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth - strech , -yHeight + leftBottomBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleTop[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth - rightTopBevel + strech , yHeight ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth + leftTopBevel + strech , yHeight,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleBottom[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth - rightBottomBevel - strech , -yHeight ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth + leftBottomBevel - strech , -yHeight,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
You've a rectangle with a width w and a height h
(-w/2, h/2) (w/2, h/2)
+----------------+
| |
| |
| |
| |
+----------------+
(-w/2, -h/2) (w/2, -h/2)
The points for the rounded corner of the rectangle are calculated by:
x = circX + cos(angle) * rad;
y = circY + sin(angle) * rad;
Then the rectangle is displaced by d. At the top d is add to the x component of the corner points and at the bottom d is subtracted from the x component of the corner points:
(-w/2 + d, h/2) (w/2 + d, h/2)
+----------------+
/ /
/ /
/ /
/ /
+----------------+
(-w/2 - d, -h/2) (w/2 - d, -h/2)
You have to apply the displacement d to the points along the arc, too. The displacement has to be scaled, in relation to the y coordinate of the point.
Points near the bottom edge have to be displaced by a larger scale, than points near the center of the left edge:
x = circX + cos(angle) * rad
y = circY + sin(angle) * rad
scale = y / (h/2)
x = x - d * scale
I am creating a UV sphere (similar to an Earth globe divided into lines of latitude). I am doing this by:
Calculating all of the vertices around each each parallel latitude circle (e.g. 72 points per circle)
Using GL_TRIANGLE_STRIP to fill in each "slice" between each of the latitude circles.
Unfortunately I keep seeing dots on my otherwise perfect sphere.
What would cause this and how do I get rid of it?
void CSphere2::AddVertices( void )
{
#define SPHERE2_RES 72
// Create sphere using horizontal slices/circles
int nPointsPerCircle = SPHERE2_RES;
int nStackedCircles = SPHERE2_RES;
GLfloat r = m_Size;
GLfloat yAngle = - (PI / 2.0f); // Start at -90deg and work up to +90deg (south to north pole)
GLfloat yAngleStep = PI / nStackedCircles;
// Sweep angle is zero initially for pointing towards me (-Z direction)
GLfloat horizSweepAngle = 0;
GLfloat horizSweepStep = ( 2 * PI ) / nPointsPerCircle;
// Each time we have a slice, the top and bottom radii vary..
GLfloat sweepRadiusTop;
GLfloat sweepRadiusBottom;
GLfloat xBottomPoint;
GLfloat zBottomPoint;
GLfloat xTopPoint;
GLfloat zTopPoint;
for( int c = 0; c < nStackedCircles; c ++ )
{
// Draw a circle - note that this always uses two circles - a top and bottom circle.
GLfloat yBottomCircle;
GLfloat yTopCircle;
yTopCircle = r * sin( yAngle + yAngleStep );
yBottomCircle = r * sin( yAngle );
std::vector<GLfloat> vBottom_x;
std::vector<GLfloat> vBottom_z;
std::vector<GLfloat> vTop_x;
std::vector<GLfloat> vTop_z;
sweepRadiusTop = r * cos( yAngle + yAngleStep );
sweepRadiusBottom = r * cos( yAngle );
// Add 1 face - a triangle strip per slice..
AddFace();
m_Faces[ c ].m_DrawType = GL_TRIANGLE_STRIP;
// Now work out the position of the points around each circle - bottom points will always be the
// same as the last top circle points.. but I'm not going to try optimising yet..
for( int s = 0; s < nPointsPerCircle; s ++ )
{
GLfloat xBottomPoint = sweepRadiusBottom * sin( horizSweepAngle );
GLfloat zBottomPoint = sweepRadiusBottom * cos( horizSweepAngle );
GLfloat xTopPoint = sweepRadiusTop * sin( horizSweepAngle + horizSweepStep );
GLfloat zTopPoint = sweepRadiusTop * cos( horizSweepAngle + horizSweepStep );
vBottom_x.push_back( xBottomPoint );
vBottom_z.push_back( zBottomPoint );
vTop_x.push_back( xTopPoint );
vTop_z.push_back( zTopPoint );
horizSweepAngle += horizSweepStep;
}
// OPTIMISE THIS!!
for( int s = 1; s <= nPointsPerCircle + 1; s ++ )
{
if( s == nPointsPerCircle + 1 )
{
// Join the last bottom point with the very first top point - go one more to fully close and leave no vertical gap
xTopPoint = vTop_x[ 1 ];
zTopPoint = vTop_z[ 1 ];
xBottomPoint = vBottom_x[ 0 ];
zBottomPoint = vBottom_z[ 0 ];
}
else
if( s == nPointsPerCircle )
{
// Join the last bottom point with the very first top point
xTopPoint = vTop_x[ 0 ];
zTopPoint = vTop_z[ 0 ];
xBottomPoint = vBottom_x[ s - 1 ];
zBottomPoint = vBottom_z[ s - 1 ];
}
else
{
xTopPoint = vTop_x[ s ];
zTopPoint = vTop_z[ s ];
xBottomPoint = vBottom_x[ s - 1 ];
zBottomPoint = vBottom_z[ s - 1 ];
}
// Calculate and add the Normal for each vertex.. Normal for a point on surface of a Sphere2 should be the unit vector going from centre
// of the Sphere2 to the surface (x,y,z).
//
// If centre of Sphere2 is 0,0,0 then N = | {x,y,z} - {0,0,0} | = | {x,y,z} |
glm::vec3 vNormalBottom = glm::vec3( xBottomPoint, yBottomCircle, zBottomPoint );
vNormalBottom = glm::normalize( vNormalBottom );
glm::vec3 vNormalTop = glm::vec3( xTopPoint, yTopCircle, zTopPoint );
vNormalTop = glm::normalize( vNormalTop );
// Add bottom of slice vertex..
m_Faces[ c ].AddVertexWithNormal( xBottomPoint, yBottomCircle, zBottomPoint, vNormalBottom.x, vNormalBottom.y, vNormalBottom.z );
// Add top of slice vertex, next step position..
m_Faces[ c ].AddVertexWithNormal( xTopPoint, yTopCircle, zTopPoint, vNormalTop.x, vNormalTop.y, vNormalTop.z );
}
int nVertexCount = m_Faces[ c ].m_Vertices.size();
m_Faces[ c ].m_SideCount = nVertexCount;
// Face colouring colours the vertices so they need to be created first..
m_Faces[ c ].SetRGB( m_RGBA.r, m_RGBA.g, m_RGBA.b );
yAngle += yAngleStep;
}
}
void CSphere2::Create( GLfloat fSize )
{
m_Size = fSize;
// Must add vertices first..
AddVertices();
glGenBuffers( 1, &m_VBO );
glBindBuffer( GL_ARRAY_BUFFER, m_VBO );
int nFaces = m_Faces.size();
int nVertexCount = 0;
for( int f = 0; f < nFaces; f ++ )
{
nVertexCount += m_Faces[ f ].m_Vertices.size();
m_Faces[ f ].m_SideCount = nVertexCount;
}
// Define the size of the buffer..
glBufferData( GL_ARRAY_BUFFER, sizeof( COLVERTEX ) * nVertexCount, NULL, GL_STATIC_DRAW );
int nOffset = 0;
for( int f = 0; f < nFaces; f ++ )
{
// Copy in each vertice's data..
for( int v = 0; v < (int) m_Faces[ f ].m_Vertices.size(); v ++ )
{
glBufferSubData( GL_ARRAY_BUFFER, nOffset, sizeof( COLVERTEX ), &m_Faces[ f ].m_Vertices[ v ].m_VertexData );
nOffset += sizeof( COLVERTEX );
}
}
glBindBuffer( GL_ARRAY_BUFFER, 0 );
}
I had the same problem with other examples that I'd copied from elsewhere so I sat down, did the math myself and I still have the same problem.
Vertex shader:
char *vs3DShader =
"#version 140\n"
"#extension GL_ARB_explicit_attrib_location : enable\n"
"layout (location = 0) in vec3 Position;"
"layout (location = 1) in vec4 color;"
"layout (location = 2) in vec3 aNormal;"
"out vec4 frag_color;"
"out vec3 Normal;"
"out vec3 FragPos;"
"uniform mat4 model;"
"uniform mat4 view;"
"uniform mat4 projection;"
"void main()"
"{"
" FragPos = vec3(model * vec4(Position, 1.0));"
" gl_Position = projection * view * vec4(FragPos, 1.0);"
// Rotate normals with respect to current Model matrix (object rotation).
" Normal = mat3( transpose( inverse( model ) ) ) * aNormal; "
" // Pass vertex color to fragment shader.. \n"
" frag_color = color;"
"}"
;
Fragment shader:
char *fs3DShader =
"#version 140\n"
"in vec4 frag_color;"
"in vec3 Normal;"
"in vec3 FragPos;"
"out vec4 FragColor;"
"uniform vec3 lightPos; "
"uniform vec3 lightColor; "
"void main()"
"{"
" // ambient\n"
" float ambientStrength = 0.1;"
" vec3 ambient = ambientStrength * lightColor;"
" // diffuse \n"
" vec3 norm = normalize(Normal);"
" vec3 lightDir = normalize(lightPos - FragPos);"
" float diff = max(dot(norm, lightDir), 0.0);"
" vec3 diffuse = diff * lightColor;"
" vec3 result = (ambient + diffuse) * frag_color;"
" FragColor = vec4(result, 1.0);"
"}"
;
Am I missing some sort of smoothing option? I have tried moving my viewpoint to both sides of the sphere and the dots are happening all around - so it isn't where the triangle strip band "closes" that's the problem - its all over the sphere.
See bright dots below:
Update: I just wanted to prove that the wrapping back to zero degrees isn't the problem. Below is an image when only a quarter of each circle is swept through 90 degrees. The dots are still appear in the mid regions.
Floating point accuracy is not infinite, when working with transcendental numbers you will inevitably accumulate errors.
Here is an example program that does the same loop that your program does, except it just prints out the final angle:
#include <cmath>
#include <cstdio>
int main() {
const int N = 72;
const float step = std::atan(1.0f) * 8 / N;
float x = 0.0f;
for (int i = 0; i < N; i++) {
x += step;
}
std::printf("x - 2pi = %f\n", x - 8 * std::atan(1.0f));
return 0;
}
On my system, it prints out -0.000001. Close to zero, but not zero.
If you want two points in your mesh to line up, don't give them different values. Otherwise you get small seams like this.
A typical approach to this problem is to just generate a circle like this:
#include <cmath>
#include <cstdio>
#include <vector>
struct vec2 { float x, y; };
int main() {
const int N = 72;
const float step = std::atan(1.0f) * 8 / N;
std::vector<vec2> circle;
for (int i = 0; i < N; i++) {
float a = i * step;
circle.push_back({ std::cos(a), std::sin(a) });
}
return 0;
}
At every point in the circle, circle[i], the next point is now just circle[(i+1)%N]. This ensures that the point after circle[N-1] will always be exactly the same as circle[0].
I found a couple of problems with the vertex calculation in the question. Since I was calculating both bottom and top vertices every time I was sweeping around a horizontal slice there was rounding/precision error produced. A point on the top of the current slice should be the same as the bottom point on the next slice up - but I was calculating this top and bottom after incrementing as Dietrich Epp suggested. This resulted in different values. My solution was to re-use the previous top circle vertices as the bottom vertices of the next slice up.
I also hadn't calculated the x/z positions for top and bottom circles using the same sweep angle - I'd incremented the angle which I shouldn't have done.
So fundamentally, problem was caused by 2 overlapping vertices that should have had identical coordinates but were ever so slightly different.
Here's the working solution:
void CSphere2::AddVertices( void )
{
#define SPHERE2_RES 72
// Create sphere using horizontal slices/circles
int nPointsPerCircle = SPHERE2_RES;
int nStackedCircles = SPHERE2_RES;
GLfloat r = m_Size;
GLfloat yAngle = - (PI / 2.0f); // Start at -90deg and work up to +90deg (south to north pole)
GLfloat yAngleStep = PI / nStackedCircles;
// Sweep angle is zero initially for pointing towards me (-Z direction)
GLfloat horizSweepAngle = 0;
GLfloat horizSweepStep = ( 2 * PI ) / nPointsPerCircle;
// Each time we have a slice, the top and bottom radii vary..
GLfloat sweepRadiusTop;
GLfloat sweepRadiusBottom;
GLfloat xBottomPoint;
GLfloat zBottomPoint;
GLfloat xTopPoint;
GLfloat zTopPoint;
std::vector<GLfloat> vCircle_x;
std::vector<GLfloat> vCircle_z;
std::vector<GLfloat> vLastCircle_x;
std::vector<GLfloat> vLastCircle_z;
int nFace = 0;
for( int c = 0; c <= nStackedCircles + 1; c ++ )
{
// Draw a circle - note that this always uses two circles - a top and bottom circle.
GLfloat yBottomCircle;
GLfloat yTopCircle;
yTopCircle = r * sin( yAngle + yAngleStep );
yBottomCircle = r * sin( yAngle );
sweepRadiusTop = r * cos( yAngle );
GLfloat xCirclePoint;
GLfloat zCirclePoint;
horizSweepAngle = 0;
vCircle_x.clear();
vCircle_z.clear();
// Now work out the position of the points around each circle - bottom points will always be the
// same as the last top circle points..
for( int s = 0; s < nPointsPerCircle; s ++ )
{
zCirclePoint = sweepRadiusTop * sin( horizSweepAngle );
xCirclePoint = sweepRadiusTop * cos( horizSweepAngle );
vCircle_x.push_back( xCirclePoint );
vCircle_z.push_back( zCirclePoint );
horizSweepAngle += horizSweepStep;
}
if( c == 0 )
{
// First time around there is no last circle, so just use the same points..
vLastCircle_x = vCircle_x;
vLastCircle_z = vCircle_z;
// And don't add vertices until next time..
continue;
}
// Add 1 face - a triangle strip per slice..
AddFace();
m_Faces[ nFace ].m_DrawType = GL_TRIANGLE_STRIP;
for( int s = 1; s <= nPointsPerCircle + 1; s ++ )
{
if( s == nPointsPerCircle + 1 )
{
// Join the last bottom point with the very first top point
xTopPoint = vCircle_x[ 1 ];
zTopPoint = vCircle_z[ 1 ];
xBottomPoint = vLastCircle_x[ 0 ];
zBottomPoint = vLastCircle_z[ 0 ];
}
else
if( s == nPointsPerCircle )
{
// Join the last bottom point with the very first top point
xTopPoint = vCircle_x[ 0 ];
zTopPoint = vCircle_z[ 0 ];
xBottomPoint = vLastCircle_x[ s - 1 ];
zBottomPoint = vLastCircle_z[ s - 1 ];
}
else
{
xTopPoint = vCircle_x[ s ];
zTopPoint = vCircle_z[ s ];
xBottomPoint = vLastCircle_x[ s - 1 ];
zBottomPoint = vLastCircle_z[ s - 1 ];
}
// Calculate and add the Normal for each vertex.. Normal for a point on surface of a Sphere2 should be the unit vector going from centre
// of the Sphere2 to the surface (x,y,z).
//
// If centre of Sphere2 is 0,0,0 then N = | {x,y,z} - {0,0,0} | = | {x,y,z} |
glm::vec3 vNormalBottom = glm::vec3( xBottomPoint, yBottomCircle, zBottomPoint );
vNormalBottom = glm::normalize( vNormalBottom );
glm::vec3 vNormalTop = glm::vec3( xTopPoint, yTopCircle, zTopPoint );
vNormalTop = glm::normalize( vNormalTop );
// Add bottom of slice vertex..
m_Faces[ nFace ].AddVertexWithNormal( xBottomPoint, yBottomCircle, zBottomPoint, vNormalBottom.x, vNormalBottom.y, vNormalBottom.z );
// Add top of slice vertex, next step position..
m_Faces[ nFace ].AddVertexWithNormal( xTopPoint, yTopCircle, zTopPoint, vNormalTop.x, vNormalTop.y, vNormalTop.z );
}
// Now copy the current circle x/y positions as the last circle positions (bottom circle)..
vLastCircle_x = vCircle_x;
vLastCircle_z = vCircle_z;
int nVertexCount = m_Faces[ nFace ].m_Vertices.size();
m_Faces[ nFace ].m_SideCount = nVertexCount;
// Face colouring colours the vertices so they need to be created first..
m_Faces[ nFace ].SetRGB( m_RGBA.r, m_RGBA.g, m_RGBA.b );
yAngle += yAngleStep;
nFace ++;
}
}
Here is what I need:
Given a point(x,y,z) in 3d space, and a mesh compose of some vertices(x,y,z), to calculate and return the close point coordinate on that mesh.
The function probably like this:
bool closePointOnMesh(const Point& queryPoint, const Mesh& myMesh, float maxDistance);
I have done some searching, and probably I will choose octree to reduce the calculation.
But there are still many details that I can't understand:
1: How the octree node been subdivided, so each node contains may contains 0~some triangles? It is easier to subdivided the cell further based on vertices and just store vertices directly.
2: How the octree structure helps to reduce the calculation, I know if the cell is empty I will just disregard it. But do I need to get all the closest point within each triangle face in a octree cell to the queryPoint, so I finally get the most closest point of all? that sound still heavy. Beside it will be more easier if I just iter through all the triangles, get the closest point from them, which means no need for the octree???
3: Is there a fast way to get the closest point to a point within a triangle face?
4: how the maxDistance limit helps to reduce the calculation?
For #3, here's some code on how to get the closest point of a triangle. It projects the point onto the triangle's plane, and then clamps the barycentric coordinates to [0,1], and uses those values computes the closest point.
Copied below:
vector3 closesPointOnTriangle( const vector3 *triangle, const vector3 &sourcePosition )
{
vector3 edge0 = triangle[1] - triangle[0];
vector3 edge1 = triangle[2] - triangle[0];
vector3 v0 = triangle[0] - sourcePosition;
float a = edge0.dot( edge0 );
float b = edge0.dot( edge1 );
float c = edge1.dot( edge1 );
float d = edge0.dot( v0 );
float e = edge1.dot( v0 );
float det = a*c - b*b;
float s = b*e - c*d;
float t = b*d - a*e;
if ( s + t < det )
{
if ( s < 0.f )
{
if ( t < 0.f )
{
if ( d < 0.f )
{
s = clamp( -d/a, 0.f, 1.f );
t = 0.f;
}
else
{
s = 0.f;
t = clamp( -e/c, 0.f, 1.f );
}
}
else
{
s = 0.f;
t = clamp( -e/c, 0.f, 1.f );
}
}
else if ( t < 0.f )
{
s = clamp( -d/a, 0.f, 1.f );
t = 0.f;
}
else
{
float invDet = 1.f / det;
s *= invDet;
t *= invDet;
}
}
else
{
if ( s < 0.f )
{
float tmp0 = b+d;
float tmp1 = c+e;
if ( tmp1 > tmp0 )
{
float numer = tmp1 - tmp0;
float denom = a-2*b+c;
s = clamp( numer/denom, 0.f, 1.f );
t = 1-s;
}
else
{
t = clamp( -e/c, 0.f, 1.f );
s = 0.f;
}
}
else if ( t < 0.f )
{
if ( a+d > b+e )
{
float numer = c+e-b-d;
float denom = a-2*b+c;
s = clamp( numer/denom, 0.f, 1.f );
t = 1-s;
}
else
{
s = clamp( -e/c, 0.f, 1.f );
t = 0.f;
}
}
else
{
float numer = c+e-b-d;
float denom = a-2*b+c;
s = clamp( numer/denom, 0.f, 1.f );
t = 1.f - s;
}
}
return triangle[0] + s * edge0 + t * edge1;
}
I want to draw a thick Arc(something like colored segment of analog dial) using polygon. For that i have added vertices in polygon and its working fine for the outer circumference BUT its joining the ends for inner circumference(the concave side).
The same logic works fine if I add those vertices in Line, but that creates an empty/non-filled arc.
My logic of adding vertices is :
for( float i = m_segmentVertex.size() - 1; i < vCount; i++ )
{
float x1 = (m_segmentVertex[ i ].x ) * cosA - m_segmentVertex[ i ].y * sinA;
float y1 = (m_segmentVertex[ i ].x ) * sinA + m_segmentVertex[ i ].y * cosA;
addVertex( vec3( x1, y1, 0.0f ) );
}
Be aware that GL_POLYGON only works with convex polygons.
You'll have to triangulate concave polygons.
Try using a triangle fan and making the center of your dial the first point.
Possibly addVertex( vec3( 0.0f, 0.0f, 0.0f ) ); before your loop.
I'd also recommend making i an int or unsigned int, a float here doesn't make sense.
This is how I created the polygon dynamically by triangulating it :
//create thick colored segments
void CreateArcMesh( float sAngle, float eAngle, vec4 color, int thickness, int radius )
{
ObjectMeshDynamic meshObj = new ObjectMeshDynamic();
vec3 vertex[0];
float dAngle = ( ( eAngle - sAngle ) / ( VERTEX_COUNT / 2.0f ) );
float cosA = cos( DEG2RAD * dAngle );
float sinA = sin( DEG2RAD * dAngle );
meshObj.setMaterial( "material_base", "*" );
meshObj.setProperty( "surface_base", "*" );
meshObj.setMaterialParameter( "diffuse_color", color, 0 );
//Add the material on both side as the indices for Triangle strip start from last vertex added
Material material = meshObj.getMaterialInherit(0);
material.setTwoSided( 1 );
meshObj.addTriangleStrip( VERTEX_COUNT + 2 );
vec3 startPos = vec3( radius * cos( DEG2RAD * sAngle ), radius * sin( DEG2RAD * sAngle ), 0.0f );
vertex.append( startPos );
vec3 secondPos = vec3( ( radius - thickness ) * cos( DEG2RAD * sAngle ), ( radius - thickness ) * sin( DEG2RAD * sAngle ), 0.0f );
vertex.append( secondPos );
float x1 = startPos.x * cosA - startPos.y * sinA;
float y1 = startPos.x * sinA + startPos.y * cosA;
vertex.append( vec3( x1, y1, 0.0f ) );
x1 = secondPos.x * cosA - secondPos.y * sinA;
y1 = secondPos.x * sinA + secondPos.y * cosA;
vertex.append( vec3( x1, y1, 0.0f ) );
forloop( int k = 0 ; VERTEX_COUNT + 2 )
{
x1 = ( vertex[ vertex.size() - 2 ].x ) * cosA - vertex[ vertex.size() - 2 ].y * sinA;
y1 = ( vertex[ vertex.size() - 2 ].x ) * sinA + vertex[ vertex.size() - 2 ].y * cosA;
vertex.append( vec3( x1, y1, 0.0f ) );
meshObj.addVertex( vertex[k] );
}
vertex.clear();
meshObj.updateBounds();
meshObj.flush();
}
I have a square area on which I have to determine where the mouse pointing.
With D3DXIntersectTri I can tell IF the mouse pointing on it, but I have trouble calculating the x,y,z coordinates.
The drawing from vertex buffer, which initialized with the vertices array:
vertices[0].position = D3DXVECTOR3(-10, 0, -10);
vertices[1].position = D3DXVECTOR3(-10, 0, 10);
vertices[2].position = D3DXVECTOR3( 10, 0, -10);
vertices[3].position = D3DXVECTOR3( 10, 0, -10);
vertices[4].position = D3DXVECTOR3(-10, 0, 10);
vertices[5].position = D3DXVECTOR3( 10, 0, 10);
I have this method so far, this is not giving me the right coordinates (works only on a small part of the area, near two of the edges and more less accurate inside):
BOOL Area::getcoord( Ray& ray, D3DXVECTOR3& coord)
{
D3DXVECTOR3 rayOrigin, rayDirection;
rayDirection = ray.direction;
rayOrigin = ray.origin;
float d;
D3DXMATRIX matInverse;
D3DXMatrixInverse(&matInverse, NULL, &matWorld);
// Transform ray origin and direction by inv matrix
D3DXVECTOR3 rayObjOrigin,rayObjDirection;
D3DXVec3TransformCoord(&rayOrigin, &rayOrigin, &matInverse);
D3DXVec3TransformNormal(&rayDirection, &rayDirection, &matInverse);
D3DXVec3Normalize(&rayDirection,&rayDirection);
float u, v;
BOOL isHit1, isHit2;
D3DXVECTOR3 p1, p2, p3;
p1 = vertices[3].position;
p2 = vertices[4].position;
p3 = vertices[5].position;
isHit1 = D3DXIntersectTri(&p1, &p2, &p3, &rayOrigin, &rayDirection, &u, &v, &d);
isHit2 = FALSE;
if(!isHit1)
{
p1 = vertices[0].position;
p2 = vertices[1].position;
p3 = vertices[2].position;
isHit2 = D3DXIntersectTri(&p1, &p2, &p3, &rayOrigin, &rayDirection, &u, &v, &d);
}
if(isHit1)
{
coord.x = 1 * ((1-u-v)*p3.x + u*p3.y + v*p3.z);
coord.y = 0.2f;
coord.z = -1 * ((1-u-v)*p1.x + u*p1.y + v*p1.z);
D3DXVec3TransformCoord(&coord, &coord, &matInverse);
}
if(isHit2)
{
coord.x = -1 * ((1-u-v)*p3.x + u*p3.y + v*p3.z);
coord.y = 0.2f;
coord.z = 1 * ((1-u-v)*p1.x + u*p1.y + v*p1.z);
D3DXVec3TransformCoord(&coord, &coord, &matWorld);
}
return isHit1 || isHit2;
}
Barycentric coordinates don't work the way you used them. u and v define the weight of the source vectors. So if you want to calculate the hit point, you will have to compute
coord = u * p1 + v * p2 + (1 - u - v) * p3
Alternatively you can use the d ray parameter:
coord = rayOrigin + d * rDirection
Both ways should result in the same coordinate.