Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - fun2code

Pages: [1]
1
Hi all!
I'm adding this post for those who are curious about how I handled the motion of the pendulum in the 1st video. It goes to the limits of my abilities in several ways.
I had an opportunity while working 3rd shift for a couple of years to finally
take on that long overdue review of classical mechanics. I had (typically) 6 hours per shift of free time, and it was nice being paid $22/hour to do it.
I obtained and studied from: Introduction to Classical Mechanics by David Morin. 1st edition (in case there's a 2nd by now).
My educational background: BS in physics long ago.
I'm using skills which would have faded to nothing by now if not for my usage in this hobby.
My goal was to make it through chapter 6 on the Lagrangian method. It's an alternative formulation (to vector based Newtonian mechanics) based on scalar quantities (just a number) such as kinetic and potential energy and is well suited to problems where motion is limited by a constraint, such as being limited to motion on the surface of a sphere in 3d space like this pendulum.

I highly recommend the textbook. It is as hardcore as anyone could want and it's loaded with fully worked solutions for a portion of the very full range of problems in each chapter. It's also loaded with clever little physics limerics. I'll post 1 or 2 here later.
This text has a place of honor in my work here. It can be seen floating in midair in the room in the 2nd video.

Please find attached type persPendulum. It's virtual update() has a bit more than usual going on, which gets right away to an issue with the enormous time step between frames, and to why type vec3d became involved.
My simplistic numerical integration scheme is this:

step 1: obtain the acceleration to be applied this frame.
In the case of the shots the acceleration is constant = (0,g,0).
In the case of the pendulum it's far from that.

step 2:
update the position:
position += velocity*dt + 0.5f*acceleration*dt*dt;
This is from Taylors series to 2 terms.
Then update the velocity
velocity += acceleration*dt;

The analysis will be in spherical coordinates.
So far these methods are persPendulum methods only, but I think they also
should be static methods available in vec3d?
// base vectors in spherical coordinates
vec3d get_rHat()const{ return sin(th)*( cos(phi)*Xu + sin(phi)*Yu ) - cos(th)*Zu; }
vec3d get_thHat()const{ return cos(th)*( cos(phi)*Xu + sin(phi)*Yu ) + sin(th)*Zu; }
vec3d get_phiHat()const{ return cos(phi)*Yu - sin(phi)*Xu; }
 
Where the vectors Xu, Yu, Zu are the cartesian basis.
I'll also attach my solution on paper soon, but I want it to be much cleaner than the working version I have.

The identifiers for the two angles theta (polar) and phi (azimuthal) and their derivatives are:
// polar angle and 1st and 2nd time derivatives
double th, th_1, th_2;
// azimuthal angle and 1st and 2nd time derivatives
double phi, phi_1, phi_2;

The 2 equations of motion obtained by the Lagrangian method are:
// step 1: find acceleration
th_2 = phi_1*phi_1*sinTh*cosTh - GravY*sinTh/L;// for theta
phi_2 = -2.0*th_1*phi_1*cosTh/sinTh;// for phi
 
Here, sinTh, cosTh are saved values of sin(th), cos(th), GravY = gravity.y and L = length of support. Note that the mass isn't a factor.

An additional condition may also be applied, perhaps to stabilize the integration?
Because the Lagrangian = kinetic - potential energy doesn't depend explicitly on the angle phi, there is a constant of the motion. Here it's the z component of the angular momentum so:
phi_1 = Lz/( sin(th)*sin(th) );// based on Lz conservation
where the value of Lz is determined by initial conditions. The quantity here has been scaled from the actual Lz: Scaled this is actually Lz /= m*L*L.
So the units of measure ought to balance above.

Integration at the frame rate ( dt = 20ms ) does not give a stable result.
It became stable at 10 updates per frame, so I tripled that and called it good.
The pendulum in the video is being updated 30 times per frame.

Once all of these micro updates are done I assign bobs position and velocity.
I'm still struggling with the float/double interface here.
// bobs new velocity
        vec3d thHat = cosTh*( cosPhi*Xu + sinPhi*Yu ) + sinTh*Zu;// current thHat
        vec3d phiHat = cosPhi*Yu - sinPhi*Xu;// current phiHat
        Vel = L*( th_1*thHat + sinTh*phi_1*phiHat );
        bob.vel = Vel.to_vec3f();// bob.vel update
        vec3d rHat = sinTh*( cosPhi*Xu + sinPhi*Yu ) - cosTh*Zu;// updated rHat
        // and position
        vec3d Pos = L*rHat;
        Pos.x += static_cast<double>(topQ.pos.x);
        Pos.y += static_cast<double>(topQ.pos.y);
        Pos.z += static_cast<double>(topQ.pos.z);
        bob.setPosition( Pos.to_vec3f() );// bob update
 

I'll leave off here for now. But I must deal with the sudden change in th_1 and phi_1 which occur when poor bob gets shot.

I'm back to wrap up.
As for handling bobs hits:
I'm cheating a bit. I call hitFree() and do not account for the constraint in the collision,
so the momentum transferred isn't accurate, but it seems to do fine for effect. This serves the present purpose well enough for me. Next problem please.

Update the values for th_1 and phi_1
bobs velocity in polar coordinates is:
vec3d persPendulum::get_vel()const
{ return vec3d( L*( th_1*get_thHat() + sin(th)*phi_1*get_phiHat() ) ); }
Let vec3d Vel = bob.vel following the collision, then
Taking the dot product (of the return value above) with thHat (unit vector in theta direction) gives
Vel.dot( thHat ) = L*th_1
solving for th_1 permits the update
th_1 = Vel.dot(thHat)/L;
Take the dot product with phiHat instead to obtain
phi_1 = Vel.dot( phiHat )/( L*sin(th) );
following this I update Vel and then bob.vel
Vel = get_vel();
bob.vel = Vel.to_vec3f();

Just call
bool hit( persBall& PB, float cR, float dt );
where Cr = coefficient of restitution allows partially elastic collisions.
Why dt? It won't be moved here, just in update(). The collision test is ray based so dt is used to obtain the position in the previous frame = 2 points for a ray.

Finally, I'll admit that the integration details are well beyond my knowledge.
I think I should be using th_1 = Lz/( sin(th)*sin(th) ); instead of the 2nd acceleration equation, which actually follows from above. I'm presently experimenting with using
phi_1 = Lz/( sinTh*sinTh );
phi += phi_1*dT;
in place of
phi_2 = -2.0*th_1*phi_1*cosTh/sinTh;
phi += phi_1*dT + 0.5*phi_2*dT*dT;
phi_1 += phi_2*dT;
and it's giving a stable result for lower # of micro updates.
However, I'm once again getting runaway cases...
It's a fight to maintain stability and I don't know enough right here.
Perhaps someone who knows this part better may advise? Please?

I've also now attached my "on paper" solution.
And that's all I've got so I'll call it a wrap.
Any questions and/or suggestions are welcome.

2
Uh oh. Type shotBurst might also be interesting. It's the fragmenting shot type from the 1st video. It inherits from persBall, so it's a persBall type and can be handled like any other shot.
shotBurst doesn't contain the shots released when it "goes off". It calls a getShot function which draws from the 100 shots in the pool in use in the level. I've only 1 instance in use, but once it bursts it's done and can be fired again. In the 1st video you may see an active shot count: shots xxx. Notice this jump by 22 or so when the shell bursts. I've tested by firing into the sky until the shot pool is exhausted. Nothing bad happens. You get just the fireball if all 100 shots are in use.

edit: I've gone back and attached some math solutions which my code is based on:

perspective transformation. Basis for get_xyw() here:
https://en.sfml-dev.org/forums/index.php?topic=29412.msg181195#msg181195

The camera basis rotations both vector and matrix based.
and Firing angle and velocities for aimed shots. Here:
https://en.sfml-dev.org/forums/index.php?topic=29412.msg181199#msg181199

Collision tests: ball vs rectangle, moving ball vs fixed ball and both balls free. Here:
https://en.sfml-dev.org/forums/index.php?topic=29412.msg181198#msg181198

3
Lastly, persTable requires persChair. This is the composite type mentioned earlier.
and persRoom #include wall, door and table.

4
persChair and persDoor #include persPt only.
One more post to follow.

5
Continuing with persTrail and persWall to finish with those having direct dependence on persLine

6
Hi again all. This seems like the right time to attach any further files for those interested. This thread is still on top so I won't be bumping it, which I won't want to do without good reason once it's no longer on top.
I'm attaching files for some of the composite types, in order of dependencies which I will remark on as needed.
This order:

Typename    required #includes
-----------     --------------------
persLine         persPt (and all below), vec3d
persTrail         persLine
persWall         persLine

persChair        persPt only
persDoor         persPt only
persTable        persChair
persRoom       persTable, persWall and persDoor

I'm not sure why vec3d, but it includes vec2d, where vec3 is also defined
You get the picture. It's not spectacularly well organized. There are signs of rushed development throughout.
I was hesitant to present persLine because honestly, I can't understand how it's working by looking at the code. I must have had a certain highly tuned picture in mind when I wrote it. It works.
Hopefully someone finds them interesting.

7
Hi. Thank you for that information Hapax. I'm very interested in that.
I followed your Selba Ward link. I'll be back...
I've come here today to continue with presenting this so that someone could run with it, design their own derived persPt types and continue. You may note the wireframe boxes in the 1st video for instance: A type with 24 sf::Vertex (for drawing 12x sf:Lines)  mapped to 8 vec3f points in 3d space.
The following will only become more evident the  more I show...
My focus on the mechanics...I've learned only enough about how to use SFML to accomplish the immediate need. Links to tutorials will be followed.

OK. On to my planned post, which begins with a closer look at how z ordering is supported in the persPt class. I've changed 2 lines in the previous declaration to make a relationship clear:
bool operator<( const persPt& rPt ){ return getDistance() < rPt.getDistance(); }
static bool compare( persPt* pPtA, persPt* pPtB ) { return *pPtB < *pPtA; }// for std::sort() on a container of pointers.
 
The compare() is for use as the 3rd argument in a call to std::sort() on a container of pointers to type persPt. The definition of operator < determines the "z order" = sorted by distance in front of the camera.
Which is almost all I got to say about that.
This line which is commented out in the 1st definition of get_xyw
vec3f L = ( Z0/Rcp.mag() )*Rcp_perp;
would base the scaling on the distance from the camera, which may produce a better peripheral scaling effect. I tried and found 2 things:
1. No noticeable improvement
2. It breaks type persLine. I haven't explored why
So the current option is chosen.

As before I'l just drop the declaration.
class persQuad : public persPt
{
    public:// zero encapsulation discipline on display

    // data members
    vec3f pt[4];
    vec3f Nu;// unit vector perpendicular to Quad
    sf::Vertex vtx[4];
    const sf::Texture* pTxt = nullptr;
    float w, h;// width and height of the quad
    bool facingCamera = false;// enables "billboarding"

    // function members
    vec3f getPosition()const{ return ( pt[0] + pt[2] )/2.0f; }
    virtual void update( float dt );
    virtual void draw( sf::RenderTarget& RT ) const;
    virtual void setPosition( vec3f Pos );
    void setNu( vec3f nu );// to default orientation: w side is horizontal, quad is tilted upwards
    void setOrientation( vec3f nu, vec3f Tu );// general orientation: pt[0], pt[1] toward Tu

    // Ta: &#39;R&#39; = rotate 90 degrees, Tb = 0, 1, 2, 3 = # times
    // Ta = &#39;F&#39; = flip image around Tb = &#39;X&#39; or &#39;Y&#39; axis
    void setTxtRect( sf::IntRect srcRect, char Ta, char Tb );// assigns texCoords with some mapping variability
    void flip( char XorY );// sometimes the required orientation needs a flip only

    // returns true when hit and writes collision point = P and unit vec3f = vu in reflected direction
    bool hit( vec3f posA, vec3f posB, vec3f& P, vec3f& vu )const;// also persBox_quad
    bool isSighted( float& dist )const;// true if aimed at. Writes distance to aim point on persQuad
    void setColor( sf::Color color );

    persQuad(){}
    virtual ~persQuad(){}

    void init( std::istream& is, sf::Texture* p_Txt = nullptr );
    persQuad( std::istream& is, sf::Texture* p_Txt = nullptr ){ init( is, p_Txt ); }

    void init( vec3f Pos, float W, float H, vec3f nu, sf::Color color, const sf::Texture* p_Txt = nullptr );
    persQuad( vec3f Pos, float W, float H, vec3f nu, sf::Color color, sf::Texture* p_Txt = nullptr )
    { init( Pos, W, H, nu, color, p_Txt ); }
};
 
Most of the data members serve an obvious purpose. The setPosition and update functions are covered. Here's the draw function:
void persQuad::draw( sf::RenderTarget& RT ) const
{
    if( !doDraw ) return;
    if( pTxt ) RT.draw( vtx, 4, sf::Quads, pTxt );
    else RT.draw( vtx, 4, sf::Quads );
}
 
In the setNu and setOrientation functions a vector basis is constructed for assigning the 4 pt[] positions. SetNu uses yHat as a 2nd vector whereas setOrientation uses the 2nd argument. This was added to make the drone over the coaster work.
void persQuad::setNu( vec3f nu )
{
    // from init() code
    Nu = nu;
    vec3f vw = nu.cross( persPt::yHat );
    if( vw.mag() < 0.2f )// nu too close to yh
        vw = nu.cross( persPt::zHat ); // go off of zh instead
    vw /= vw.mag();
    vec3f vh = vw.cross( nu );
    vec3f Pos = pos;
    Pos -= (w/2.0f)*vw;
    Pos -= (h/2.0f)*vh;
    pt[0].x = pt[0].y = pt[0].z = 0.0f;// lower left
    pt[1] = h*vh;// up left
    pt[3] = w*vw;// lower right
    pt[2] = pt[1] + pt[3];// up right
    for( size_t i = 0; i < 4; ++i )
    {
        pt[i] += Pos;
        vtx[i].position = persPt::get_xyw( pt[i] );
    }
}

// unit length of nu and Tu is assumed here
void persQuad::setOrientation( vec3f nu, vec3f Tu )
{
    Nu = nu;
    vec3f T1 = Nu.cross( Tu );// in W direction
    float H = 0.5f*h, W = 0.5f*w;
    pt[0] = pos + H*Tu - W*T1;
    pt[1] = pos + H*Tu + W*T1;
    pt[2] = pos - H*Tu + W*T1;
    pt[3] = pos - H*Tu - W*T1;
    for( size_t i = 0; i < 4; ++i )
        vtx[i].position = persPt::get_xyw( pt[i] );
}
 
The vector Nu is most usefull for determining which quads to draw. For example:
The windows in a room all have Nu facing outward. All windows are to be drawn when inside the room, but when outside I want to draw only the windows facing the camera.
Let PQ be an instance of a persQuad:
if( ( persPt::camPos - PQ.pos ).dot( PQ.Nu ) > 0.0f )
then draw the window.

Let's see how it also helps with collision testing, range finding, target acquisition, etc...
I've prepared another video. The method bool isSighted( float& distance ) is used to highlight the persQuad before the crosshair. distance*persPt::camDir = vector range to target. The velocities to fire are calculated (for both the high and low firing angles).
In the video I adjust the shot speed and gravity so that our position is a bit within the maximum range. The key 'D' will fire the high shot immediately then fire the low shot after a delay = calculated difference in the flight times so the shots hit simultaneously.
Both velocities are found and saved so I can change aim once the 1st shot is off. I walk double hits back and forth across a row of targets...

I'll be back to present these
bool hit( vec3f posA, vec3f posB, vec3f& P, vec3f& vu )const;// writes point hit and unit vector in reflected direction
bool isSighted( float& dist )const;// true if aimed at. Writes distance to aim point on persQuad
 
I've attached vec2f.h and vec2f.cpp, which also define type vec3f.
To eXpl0it3r: I'll be checking out those replacements for bandicam soon!

I'm back. On reflection. Presenting and explaining the above would go well beyond "introducing a project", so I've also attached persPt.h and persPt.cpp.
All of my types in use are there. I'm sorry I don't have a GitHub or similar.
I'll also be attaching pictures of my solutions on paper to some of the vector math problems involved here. Some are involved. eg.
bool persBall::hitFree( persBall& collider );
is written to conserve momentum correctly and to assign the positions so that the position of the center of mass is unchanged, which matters if a force field is applied a spontaneous jump in potential energy may matter.
etc...

edit: I've attached my collision test solutions here (earlier post w/room)
https://en.sfml-dev.org/forums/index.php?topic=29412.msg181198#msg181198

8
It isn't doing the 3d part.
This (extremely busy) function is replacing the z coordinate with a
scaling factor = Z0/U.
// get the window position from the vec3f position in the scene
sf::Vector2f persPt::get_xyw( vec3f Pos )
{
    vec3f Rcp = Pos - camPos;// from camera to Pos
    float U = Rcp.dot( camDir );// projection along camDir = "z - Zcam"
    vec3f Rcp_perp = Rcp - U*camDir;// component of Rcp in plane perpendicular to camDir
    vec3f L = ( Z0/U )*Rcp_perp;// perspective transform applied
    if( U < 0.0f ) L *= -1.0f;
    return sf::Vector2f( X0 + L.dot( xu ), Yh - L.dot( yu ) );
}
SFML gets involved at the return. It returns sf::Vector2f so an assignment can be made directly to the sf::Vertex position member.
Here's an example of use in the persQuad class ( 4 vec3f paired with 4 sf::Vertex )
void persQuad::setPosition( vec3f Pos )
{
    vec3f dPos = Pos - pos;
    pos = Pos;
    for( size_t i = 0; i < 4; ++i )
    {
        pt[i] += dPos;// the pt[i] are the 4 positions in 3d space
        vtx[i].position = persPt::get_xyw( pt[i] );// which the 4 sf::Vertex are mapped to
    }
    update_doDraw();// am I in the viewable window area?
}
I hope this squares the mystery.

Here's the persQuad::update function. It supports a "billboarding" method whereby it always faces the camera. This makes a flat image (eg. of a tree) look much more 3d.
The setNu() sets the surface normal vector and all of the vtx[] positions.
void persQuad::update( float dt )
{
    update_doDraw();
    if( !doDraw ) return;

    if( facingCamera )
    {
        vec3f N = persPt::camPos - pos;
        N /= N.mag();
        setNu( N );
    }
    else
    {
        for( size_t i = 0; i < 4; ++i )
            vtx[i].position = persPt::get_xyw( pt[i] );
    }
}

The vases on the tables in the latter video have facingCamera = true and are "billboarding" a flat image. The eye doesn't notice that the view of the flowers is always the same.

9
Thanks also for the links, I'll be following them.
I'd like to make this thread a bit more solid with some more code.
I'm going to just drop the declaration for class persPt. It's well commented.
The class includes both the "global" coordinate basis ( xHat, yHat, zHat ) and the "camera" coordinate basis ( xu, yu, camDir ) and the methods for rotating the view, to support z-ordering, etc. All code is in C++

#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

#include <vector>
#include <fstream>
#include <functional>
#include "../vec2f.h"
#include "../spriteSheet.h"

class persPt
{
    public:
    static float X0, Yh;// view point in window
    static float Z0;// for perspective scaling of x and y coordinates
    static vec3f camPos;// camera position in scene
    static vec3f camDir;// direction pointed
    static vec3f yu;    // unit vector "up" (camera orientation)
    static vec3f xu;    // unit vector "to right" : xu = camDir.cross( yu ) maintain in update()
    static const vec3f xHat, yHat, zHat;// these form the "world" or "global" coordinate vector basis
    static float angle;// yaw
    static float pitchAngle;
    static float rollAngle;

    static sf::Vector2f get_xyw( vec3f Pos );// return window position for Pos
    static float changeCamDir( float dAy, float dAp, float dAr );// returns compass heading
    static float changeCamDir_mat( float dAy, float dAp, float dAr );// uses a matrix
    static void calibrateCameraAxes();// maintenance
    static void sortByDistance( std::vector<persPt*>& pPtVec );// this hand rolled sort function is no longer used
    static bool init_stat( std::istream& is );// initialize static members
    static bool compare( persPt* pPtA, persPt* pPtB );// for std::sort() on a container of pointers. This is operator <

    vec3f pos;// position in scene
    float Rbound = 1.0f;// boundary in front of camera. Closer = don&#39;t draw
    bool doDraw = true;// calculate in update() and use in zSort() and draw()
    bool isMoving = false;// no need to update position of object while off screen or behind camera

    void init( vec3f Pos ){ pos = Pos; }
    sf::Vector2f get_xyw()const { return persPt::get_xyw( pos ); }
    float getDistance()const{ return ( pos - persPt::camPos ).mag(); }
    virtual void setPosition( vec3f Pos ){ pos = Pos; }
    void setPosition( float X, float Y, float Z ) { setPosition( vec3f(X,Y,Z) ); }
    virtual void update( float dt ) = 0;
    virtual void draw( sf::RenderTarget& RT ) const = 0;
    virtual void update_doDraw();

    bool operator<( const persPt& rPt ){ return getDistance() < rPt.getDistance(); }

    persPt(){}
    virtual ~persPt(){}
};

The "flight" control allows input for yaw and pitch. The roll amount is calculated when auto-roll is on (default), otherwise the mouse scrollwheel allows enough roll to be applied to counter the roll induced by pitching and yawing around. The camera vector basis (xu,yu,camDir) is well suited to these
changes since:
A "yaw" motion   = rotation about the yu axis.
A "pitch" motion = rotation about the xu axis.
A "roll" motion  = rotation about the camDir axis.
The small rotations of the vector basis are handled by a function where the new basis is constructed from  the existing one.

This has gone through 2 stages.
Initially I updated each base vector for each of the 3 small (frame to frame) angular changes, thusly:

float persPt::changeCamDir( float dAy, float dAp, float dAr )// returns compass heading
{
    return changeCamDir_mat( dAy, dAp, dAr );// matrix based method now in use

    // this code works fine too
    camDir = camDir*cosf(dAy) + xu*sinf(dAy);// yaw
    xu = yu.cross( camDir );

    camDir = camDir*cosf(dAp) - yu*sinf(dAp);// pitch
    yu = camDir.cross( xu );

    yu = yu*cosf(dAr) + xu*sinf(dAr);// roll
    yu /= yu.mag();// renormalize: this has proven necessary - NEW
    xu = yu.cross( camDir );

    // compass heading
    vec3f Rh = camDir - camDir.y*yHat;// component of camDir in x,z plane
    Rh /= Rh.mag();// normalize
    float a = acosf( Rh.dot( zHat ) );
    if( Rh.dot( xHat ) < 0.0f ) a *= -1.0f;
    return a*180.0f/3.14159f;
}

And here's the matrix based method which follows straight from the above literal update assignments:

float persPt::changeCamDir_mat( float dAy, float dAp, float dAr )// returns compass heading
{
    float M[3][3];
    float SnAy = sinf(dAy), CsAy = cosf(dAy);
    float SnAp = sinf(dAp), CsAp = cosf(dAp);
    float SnAr = sinf(dAr), CsAr = cosf(dAr);

    M[0][0] = CsAr*CsAy - SnAr*SnAp*SnAy;    M[0][1] = -SnAr*CsAp;   M[0][2] = -CsAr*SnAy - SnAy*SnAp*SnAr;
    M[1][0] = SnAr*CsAy + SnAy*SnAp*CsAr;    M[1][1] = CsAr*CsAp;    M[1][2] = -SnAr*SnAy + SnAy*SnAp*CsAr;
    M[2][0] = CsAp*SnAy;                     M[2][1] = -SnAp;        M[2][2] = CsAp*CsAy;
    vec3f xcOld = xu, ycOld = yu, zcOld = camDir;

    xu     = M[0][0]*xcOld + M[0][1]*ycOld + M[0][2]*zcOld;
    yu     = M[1][0]*xcOld + M[1][1]*ycOld + M[1][2]*zcOld;
    camDir = M[2][0]*xcOld + M[2][1]*ycOld + M[2][2]*zcOld;

    // compass heading
    vec3f Rh( camDir.x, 0.0f, camDir.z );// = camDir - camDir.y*yHat;// component of camDir in x,z plane
    Rh /= Rh.mag();// normalize
    float a = acosf( Rh.dot( zHat ) );
    if( Rh.dot( xHat ) < 0.0f ) a *= -1.0f;
    return a*180.0f/3.14159f;
}

The following is being called each frame to keep the camera basis from becoming corrupted by the endless sequence of rotations.
The global basis doesn't need this. It's vectors are never varied.

void persPt::calibrateCameraAxes()
{
    persPt::camDir /= persPt::camDir.mag();
    persPt::xu = persPt::yu.cross( persPt::camDir );
    persPt::xu /= persPt::xu.mag();
    persPt::yu = persPt::camDir.cross( persPt::xu );
}

This isn't actually necessary to do every frame. It takes 10 seconds or more for visible aberrations to start creeping in without any correction.
At an early dev stage I wanted to observe these aberrations so I had a button setup to click when it got too bad, and the above code would instantly restore normalcy. A first sign is that objects near the horizon line appear on the wrong side of it.

This seems like plenty for one post, so I'll stop here for now.

edit: I've attached my solutions to the camera vector basis rotation problem.
filename: mathRotateVec_sm.jpg
The matrix based solution is based on the vector problem above. It's 2 pages.
rotateMatrix1_sm.jpg and rotateMatrix2_sm.jpg

edit2: Also attaching solution for calculating firing angles and velocities for aimed shots.
Problem is coming at end but space to attach is here: planarHitTest_sm.jpg

10
Hi. Thank you for the encouraging reply.
No, I have no plans for this aside from continuing to tinker with it.
Incidentally, according to output in the terminal, that drone was hit by the homing shot.
The fun part seems to be working the mechanics out.

I would appreciate tips re. better image sources. If the images all look like something that a 5 minute Google image search would turn up for free, it's because they all are.

In this project I dealt with the problem of how to draw a large object when a portion gas fallen behind the camera yet a portion is still visible ahead? My perspective transform gives no usefull position so I don't draw at all. This works out fine in the level posted since it takes a quite close pass to see an object wink out before you've passed it.
But what about rendering a scene within a room? Half of the room is always behind the camera! I struggled with a couple of models before I realized that I'd already solved the essential problem in the 1st perspective level. The red and green lines in the video are a special type which will move an end to keep it in front of the camera so the line can keep being drawn. A wall with 2 of these as the top and bottom edges can have sf::Vertex pinned to these variable positions and the wall surfaces are drawn. The floor and ceiling are cheats, just what wasn't drawn over. I just got a bandicam for this up too:


Note the 2 table and chair sets. One is 3 chairs (one looks like a table) plus 2 persQuad for the vases. Z ordering is failing because the 5 separate objects are too close together. The other table and chairs + vases is a single composite object which takes care of z ordering its parts properly. The 3 chairs against the wall are also owned by this table.

edit: about the see through doorways. I've rarely used sf::RenderTexture so I struggled to get images in the doorways. Only the indoor or outdoor objects are being drawn at once. I've got split update and draw functions for these 2 groups of objects and the door calls one for each side. I've got it close, but it's not quite right.

The roller coaster rails are also all type persLine. Also, the coaster is ridable. Key 'P' places the camera a bit above the track. It was time to generalize my existing 2d types for programmatic motion to 3d and the coaster is a great test bed for this.
The 3 trails in the latter video are also persLine, with the corners of the trail surface sf::Quad pinned to the magic line ends. These lines are all over now.

edit: I've attached my solutions to several upcoming problems here because there's space: These are all ray based collision tests. They depend on the position of the collider "now" ( point B) and last frame (point A).
For a fixed target:
ball vs persQuad (rectangular area): planarHitTest_sm.jpg
moving ball vs fixed ball: collideBallFixed_sm

ball vs ball both free. Velocity and position assignments are made to conserve momentum and center of mass position. If anyone knows a physics person, please check my solution. 2 pages: ballVballFree1_sm  and freeBall2_sm

11
I took this on just to see what I would get graphically, and boy did it take off! I now have my own little 3d world to do physics stuff in!
Here's a bandicam recording of play.

 It shows most of the good stuff in rapid fashion, but there's much more. I added a help menu for myself as I was losing track of keyboard shortcuts to the various features.

I'll get into some coding details here soon. Briefly, the (abstract) base class persPt (perspective point) for all 3d drawable objects has static members which build in a camera coordinate basis which are rotated to achieve yaw, pitch and roll. I've really been discovering the utility of static class members in recent projects. Here, the camera position and orientation are available in the member functions of all derived drawable types, which makes it straightforward to have each type fully manage its own state. I've achieved some optimizing in my z-ordering, eg. to sort a container of pointers to type persPt which is a shortlist of only those to be drawn this frame, etc.
I'll post some actual code shortly. I do post rarely but I've been quietly chugging away at projects using SFML. I have a solid appreciation for such an easy to use API (for the basics). For comparison, I did my 1st animated space shooter (2d) about 20 years ago.
I used Windows GDI. The OLD stuff, pre GDI+. I hated having to spend 80% of my time coding for the graphics when it's the other 20% that's the important part!
I have a memory leak bug story from that project for another time, except to say that ever since my programs allow the 'esc' key for quitting.

Edit: for further content. I'm quite lost here. I hope this is an intended means to continue. Here's a try at posting code. This is the static persPt function which transforms a 3d position to the required (x,y) position in the window.
// get the window position from the vec3f position in the scene
sf::Vector2f persPt::get_xyw( vec3f Pos )
{
    vec3f Rcp = Pos - camPos;// from camera to Pos
    float U = Rcp.dot( camDir );// projection along camDir = "z - Zcam"
    vec3f Rcp_perp = Rcp - U*camDir;// component of Rcp in plane perpendicular to camDir
    vec3f L = ( Z0/U )*Rcp_perp;// perspective transform applied
    if( U < 0.0f ) L *= -1.0f;
//    vec3f L = ( Z0/Rcp.mag() )*Rcp_perp;// perspective transform applied
    return sf::Vector2f( X0 + L.dot( xu ), Yh - L.dot( yu ) );
}
The offset is to the crosshair position = center of window. Also, the '-' sign on the y component is the only place in the project where my choice of y axis up has to resolve to graphics reality. The vec3f type is just a math vector type of mine. This project is very heavy with vector based math problems. It's been a great exercise for those skills!
I think I'd better go study a wiki here now.

edit: I've attached my solution to the math problem. The code for get_xyw() is based on it.

12
SFML projects / Spider Solitaire game with particle effects
« on: May 29, 2018, 04:24:43 pm »
Hi everyone. I wanted a version of Spider Solitaire to play which is similar to the windows XP version so I've made my own. The game ends in a fireworks show on a win so some particle effects were needed.
Particle emission and collision handling grew from this effort. Level and GUI classes are also developed.

** Summary Description **
A function graphing class is developed and used for particles to collide with. Any function can be defined in Cartesian or polar coordinates. I've also made a line emitter which prints particles like a dot matrix printer.
The full project is here: https://github.com/pfloyd123/spider-solitaire-plus-particles

The card deals and movement between columns are animated. When a facedown card is revealed a flip over animation occurs. The side lengths are varied during the flip to create some perspective, making it appear to flip left side over right. When an ace through king set is formed an animatioon collapses the set, then moves it to a complete set pile.

In the particle physics level each function graph has a control which allows rotation of the graph, variation of parameter values and for the curve to have motion.
The typename for the particles is 'spark', hence the 'spark' control in the video.

Gravity, drag, curl, spring and/or inverse square forces can be applied to the sparks.
A force control allows variance of the force magnitudes, as well as parameters for the spring forces such as spring stiffness, rest length and the position of the attraction centers.

Here's a YouTube demo. The particle interactions are shown first, followed by the card game.


** In Greater Detail **
** The spark type: see include/spark.h and .cpp
Although the image size is variable each is treated as a point object. Graphically each is an sf::Quad drawn from a texture (see dots9.png in the images folder). The sparks are not mutually interacting so we can have thousands without performing millions of collision tests each frame. They collide only with the graphFunc types. They have mass and can be acted on by applied forces. Gravity is acting in most of the demo video.
Interactions with 2 springs is also shown. The yellow dots are at the springs fixed end positions.

** The graphFuncs: see include/graphFunc.h and .cpp
Graphically, all curved examples (sine, polynomial, hyperbola, ellipse and flower) are 100 or more sf::Vertex drawn as a sf::LinesStrip.
The 2 polygon types have 1+number-of-vertices sf::Vertex, also drawn as sf::LinesStrip. The defining function can be expressed in either Cartesian or polar coordinates. The sine, polynomial and hyperbola examples are Cartesian. All others are in polar coordinates.
Sparks are collision tested when incident from either side of the graph, so sparks can be contained within the interior area of the polar types or bounced off of the outside. Both are demonstrated in the video.
Collision testing: A bounding box check is done 1st, then a ray based method is used to check if the spark has crossed the graph. Sparks have a velocity data member (an sf::Vector2f) so the position last frame can be found. If Yspark > Ygraph in one position and Yspark < Ygraph in the other, then it has crossed and must be bounced off of the incident side. I interpolate to find where it crossed the graph then place it 1 pixel from the incident side at the crossing point. The velocity component normal to the graph is assigned *= coefficient of restitution, so inelastic collisions can occur. This can be varied in the app with a 'click strip' gui object at the bottom of the window. The incident spark velocity is relative to the graph so the graph motion is accounted for in the collision. This method is working well for a fair range of parameter values (variable in each curves control) but leakage does occur in some cases. Since the algorithm is ray based it works well for sparks moving at high speed. The collision test function for the graphFunc doesn't act on any particular object type, instead it takes the object position and velocity by reference:
bool hit( sf::Vector2f& Pos, sf::Vector2f& vel, float cr, float dt, float standOff = 0.0f ) const;
Therefore, any object with a position and velocity can be collision tested against a graphFunc.

** The forces: see include/forceType.h and .cpp
The central force type is derived from an abstract conservative force type. Central forces are either a spring or an inverse square force. Since they are conservative, the force can be given as a vector quantity, or expressed as a scalar potential energy function:

virtual sf::Vector2f F( sf::Vector2f pos )const = 0;// find the force
virtual float U( sf::Vector2f pos )const = 0;// find the potential energy

There are also functions which create GUI controls for each instance. This function is used to create the spring force controls on the force control surface in the app. virtual void makeListControl( buttonList& BL );// prepare a given buttonList (see below)

There is a level in the app, not shown in the demo, where 3 methods of applying a force to sparks are compared.
1. Apply force directly.
2. Calculate force = -Gradient( U(pos) ) where partial derivatives are approximated.
3. A force map is created, then the force acting at the spark position is interpolated from the map.
Response variance is seen where forces vary rapidly with position, such as near the attraction center of an inverse square force. See include/level/lvl_spark.h and .cpp
The other forces: cross, drag and gravity are applied directly in the lvl_sparkAni::update() function.


** The GUI objects: see include/button_types
All GUI objects derive from the abstract class button, so named because it all started with just a clickable button. Here are the core interface functions:
 
virtual void draw( sf::RenderTarget& RT )const = 0;// draw the object
virtual bool hit()const = 0;// test if the mouse cursor is over it. The shape of a button can vary with the type.
virtual bool MseOver();// calls hit() and assigns a few things
virtual bool hitLeft();// respond to left click
virtual bool hitRight();// respond to right click
virtual void setPosition( sf::Vector2f Pos ) = 0;
virtual void update();// supports animated features
virtual void init_delayBox()// supports hover effects eg. value to be selected on a click strip object.

There are also some static member functions which process all GUI objects in a loop. These functions are called outside of the Level::update() function, so the only coding necessary in a level is to initialize the GUI objects. All usage is then automatic.
The buttonList (a drop list) and controlSurface types manage a set of other button objects, and serve as a proxy for them to the static management system. The click strip (typename buttonValOnHit) and slideBar types derive from an abstract floatSelector type. The multiSelector type is defined in floatSelector.h and combines a floatSelector with a std::vector<buttonRect> so a single click strip (or slideBar) can be used to set numerous quantities. It's used in most of the controls seen in the demo.
The buttStrip type is a button which opens/closes a single floatSelector. It's used in the force control to vary the cross, drag and gravity forces being applied to the sparks. When used with a slideBar it can be set to fade the value in and out.
The floatSelector types have fine-tune and value-reset features. Position the mouse cursor over the selector and use the mouse wheel to change the value by small amounts. The amount per wheel click is variable per control instance. It's used in the demo to tune the cross force applied during printer use.
A right click over a floatSelector will reset it to the initial value. It's used in the demo to stop the polynomial from flapping during the fireworks display.
There are other types (eg. color picker, OK box, radio buttons, virtual joystick), but I'll limit decsriptions here to the types used in this app.

13
Graphics / Can I reload an image to an sf::Image object?
« on: February 28, 2011, 06:49:36 am »
Thanks for the reply XDest. Those are some good ideas to go with.
I've started on an image manager class and will keep it simple as I experiment with it.

I'll have to stick with methods available in v1.6 since that's what I installed. I'll learn the basics with that before moving to v2.0

14
Graphics / Can I reload an image to an sf::Image object?
« on: February 27, 2011, 08:26:00 am »
I am working on a 2D game where the world area is large. I am thinking of using a 2D array of sf:Image objects to store portions of the background to display (tiles?).

I want to load enough images to cover the screen + an extra row above and below and an extra column to the left and right. When the view is scrolled into these bounding rows of images I would like to load another row into the Image objects left behind (then cycle the sprite positions to the front). Can I simply call LoadImageFromFile() again? The tiles are always the same size (128x128 pixel jpg images).

I find that this works but is the memory taken by the image previously loaded released when I do this?

What is a good method for maintaining an "island" of images in memory within the vicinity of the current view?

I recently started using SFML and am impressed with its ease of use, but I could not find these topics addressed in the tutorials.

Thanks.

Pages: [1]