Animating ofImage

I am new to OF and working on some animations and need a little advise to get started.

I need to animate a ball (an ofImage) being thrown from the 1st person perspective - ball gets smaller as it goes off into the distance, ideally it has a little arch to it and I can choose where it goes on screen.

I get the basics of animating position by updating the x and y in the loop. I have also used ofxTweenzor, but I am struggling to get this working well.

Is there a way that I can define a path for the ball to follow? Something else I should be trying?

Thanks!

Could you post some code? It seems like animating a bit of x/y and drawing the image smaller and smaller as time passes would give the illusion you’re looking for.

you could us ofPolyline to define your motion path in 2d or 3d and then step through all the points in ofPolyline.

ofPolyline is great because it can also be used to smooth you path, or to get in-between points that you did not define, or to step through the full length of the polyline in different step sizes.

I would recommend looking at all the option at the documentation page:
http://openframeworks.cc/documentation/graphics/ofPolyline.html

Thanks @stephanschulz ofPolyline is a great start. I now have the image moving along a path.

Right now it is only moving in 2D. Is there something I need to do to make it change size with the z coordinates?

Here’s the code…

//--------------------------------------------------------------
void ofApp::setup(){

    ofEnableDepthTest();

    polyline.curveTo(350, 100, 1);
    polyline.curveTo(350, 100, 1);
    polyline.curveTo(400, 150, 10);
    polyline.curveTo(450, 100, 20);
    polyline.curveTo(500, 150, 30);
    polyline.curveTo(550, 100, 40);
    polyline.curveTo(550, 100, 40);

    ball.loadImage("Ball.png");

    pct = 1;
}

//--------------------------------------------------------------
void ofApp::update(){
    pct++;
    if (pct > 100) {
	    pct = 1; 
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofVec3f pt = polyline.getPointAtPercent(pct / 100.0);
    ball.draw(pt);
}

looks like 40 might be too small to actually see a change is size.

i tried 400 and it did get bigger.

Hmm. I 'm just not seeing it change, even at 4000.

Is there a setting I am missing maybe?

If you are drawing an ofShape (ofCircle, for example), it will automatically resize based on distance. ofImages will not do that, and you have to resize them yourself.

Try replacing:

ball.draw(pt);

with:

ball.draw(pt, ball.getWidth() + (pt.z * ofGetWidth()/ofGetHeight()), ball.getHeight() + (pt.z * ofGetHeight()/ofGetWidth()));

Note: this code is solely meant to illustrate the point. It will cause the image to flip if you send it to a negative z value.

Edit: ok here’s a better way to do it:

float width = ofMap(pt.z, -1000, 1000, 0, ball.getWidth() * 2);
float height = ofMap(pt.z, -1000, 1000, 0, ball.getHeight() * 2);
ball.draw(pt, width, height);

This will give you a nice result without flipping.

Rockin’!!

I think I get this enough to be dangerous.

Thanks all!

as tabularasa1992 says changing the image width and height will give you the effect of the wall being closer or further.

but you can also use true 3d.

    ofPolyline polyline;
    int pct;
    ofImage ball;

    ofMesh mesh;

    void ofApp::setup(){
    
    ofEnableDepthTest();
    
    
    polyline.curveTo(350, 100, -1000);
    polyline.curveTo(350, 100, -1000);
    polyline.curveTo(400, 150, 10);
    polyline.curveTo(450, 100, 20);
    polyline.curveTo(500, 150, 30);
    polyline.curveTo(550, 100, 40);
    
    ball.loadImage("ball.png");


    int w = ball.getWidth();
    int h = ball.getHeight();
    
    mesh.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
    
    mesh.addVertex(ofVec2f(0, 0));
    mesh.addVertex(ofVec2f(w, 0));
    mesh.addVertex(ofVec2f(w, h));
    mesh.addVertex(ofVec2f(0, h));
    mesh.addVertex(ofVec2f(0, 0));
    
    mesh.addTexCoord(ofVec2f(0, 0));
    mesh.addTexCoord(ofVec2f(w, 0));
    mesh.addTexCoord(ofVec2f(w, h));
    mesh.addTexCoord(ofVec2f(0, h));
    mesh.addTexCoord(ofVec2f(0, 0));
    
}

void ofApp::update(){
    pct++;
    if (pct > 100) {
        pct = 1;
    }
    
}

   void ofApp::draw(){
     ofBackground(255, 255, 255); // background white
    
    ofVec3f pt = polyline.getPointAtPercent(pct / 100.0);

    
    ofSetColor(255);
    ofFill();
    ofPushMatrix();
    ofTranslate(pt);
    ball.getTextureReference().bind();
    mesh.draw();
    
    ball.getTextureReference().unbind();
    ofPopMatrix();
  }

While that’s true, I’m fairly certain what you wrote is essentially what open frameworks implicitly does when you call draw image. From what I can tell, ofImage.draw() calls ofImage.drawSubsection(), which calls ofGlRenderer.draw(), which in turn calls ofTexture.drawSubsection(), which seems pretty similar to what you wrote here. I’m pretty new to OF, so I’m not too sure about this, but is there any real advantage to writing it explicitly in your own code?

Here’s the function from ofTexture.cpp:

void ofTexture::drawSubsection(float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) {


GLfloat px0 = x;		// up to you to get the aspect ratio right
GLfloat py0 = y;
GLfloat px1 = w+x;
GLfloat py1 = h+y;

if (texData.bFlipTexture == ofIsVFlipped()){
	swap(py0,py1);
}

// for rect mode center, let's do this:
if (ofGetRectMode() == OF_RECTMODE_CENTER){
	px0 -= w/2;
	py0 -= h/2;
	px1 -= w/2;
	py1 -= h/2;
}

//we translate our drawing points by our anchor point.
//we still respect ofRectMode so if you have rect mode set to
//OF_RECTMODE_CENTER your anchor will be relative to that.
GLfloat anchorX;
GLfloat anchorY;

if(bAnchorIsPct){
	anchorX = anchor.x * w;
	anchorY = anchor.y * h;
}else{
	anchorX = anchor.x;
	anchorY = anchor.y;
}

px0 -= anchorX;
py0 -= anchorY;
px1 -= anchorX;
py1 -= anchorY;


// -------------------------------------------------
// complete hack to remove border artifacts.
// slightly, slightly alters an image, scaling...
// to remove the border.
// we need a better solution for this, but
// to constantly add a 2 pixel border on all uploaded images
// is insane..

GLfloat offsetw = 0.0f;
GLfloat offseth = 0.0f;

if (!ofGLSupportsNPOTTextures() && bTexHackEnabled) {
	offsetw = 1.0f / (texData.tex_w);
	offseth = 1.0f / (texData.tex_h);
}
// -------------------------------------------------

ofPoint topLeft = getCoordFromPoint(sx, sy);
ofPoint bottomRight = getCoordFromPoint(sx + sw, sy + sh);

GLfloat tx0 = topLeft.x + offsetw;
GLfloat ty0 = topLeft.y + offseth;
GLfloat tx1 = bottomRight.x - offsetw;
GLfloat ty1 = bottomRight.y - offseth;

/*if(z>0 || z<0){
	ofPushMatrix();

	ofTranslate(0,0,z);
}*/
quad.getVertices()[0].set(px0,py0,z);
quad.getVertices()[1].set(px1,py0,z);
quad.getVertices()[2].set(px1,py1,z);
quad.getVertices()[3].set(px0,py1,z);

quad.getTexCoords()[0].set(tx0,ty0);
quad.getTexCoords()[1].set(tx1,ty0);
quad.getTexCoords()[2].set(tx1,ty1);
quad.getTexCoords()[3].set(tx0,ty1);

// make sure we are on unit 0 - we may change this when setting shader samplers
// before glEnable or else the shader gets confused
/// ps: maybe if bUsingArbTex is enabled we should use glActiveTextureARB?
glActiveTexture(GL_TEXTURE0);

bind();
quad.draw();
unbind();

/*if(z>0 || z<0){
	ofPopMatrix();
}*/

}

If you want to draw it in 3D space without passing in a custom width and height, you can cut out all the custom mesh drawing and just do:

ofPushMatrix();
ofTranslate(pt);
ball.draw(0, 0);
ofPopMatrix();

The reason this is probably a better idea than passing in width and height arguments is that it will make things less confusing if you ever want to change the image size for other reasons that are not based on depth.

tabularasa1992 has a point that OF does a lot of that already internally.

also agree this easier solution is the simplest

 ofVec3f pt = polyline.getPointAtPercent(pct / 100.0);
    ofPushMatrix();
    ofTranslate(pt);
    ball.draw(0,0);
    ofPopMatrix();