Jump to content

[1.16] Smooth entity render - how it was made?


Dzuchun

Recommended Posts

Right to the question: how minecraft achieves smooth entity rendering? As far as I know and experimented with Logging of entity's position in render method, it is updated, like, 20 times per second, which equals to 1 tick.(No, it isn't!) So it seems like while rendering, minecraft uses entity's speed to determine where it should be, or what?

 

The problem is, while standing still, player's getMotion().toString() returns (0.0, -0.0784000015258789, 0.0). Is this really gives velocity? Is -0.0784... a deviation? If so, what is a unit velocity here? 1 block/tick? 1 px/tick? (Here by px i mean 1/16 block)

 

Ok, here is the problem I met. I need to render an entity at a fixed position for the player, as if it is a part of it.

Here is my render method in renderer class:

public void render(MyEntity entityIn, float entityYaw, float partialTicks, MatrixStack matrixStackIn,
            IRenderTypeBuffer bufferIn, int packedLightIn) {
        if (entityIn.getOwner() != null) {
            PlayerEntity owner = entityIn.world.getPlayerByUuid(entityIn.getOwner().getUniqueID()); //Will optimize that later, nevermind. Owner is a player I should stick to.
//            entityIn.setRawPosition(owner.getPosX(), owner.getPosY(), owner.getPosZ()); (first try to stick entity to player, resulted 20 times per second jumping)
            LOG.info("Entity's position: {}", entityIn.getPositionVec().toString()); //Used to see, that position updates every 3 frames with 60FPS.
            matrixStackIn.push();
//            Vector3d move = owner.getPositionVec().add(entityIn.getPositionVec().scale(-1)).add(owner.getMotion().scale(partialTicks-1f)); //Uses player motion, so while standing still, entity vibrates. I'm sure it's caused my motion, because without last component entity stops vibrating.
//            Vector3d move = owner.getMotion().scale(partialTicks);
          //Upwards you may see other attempts to achieve a goal. None worked.
            Vector3d move = owner.getPositionVec().add(entityIn.getPositionVec().scale(-1)); //
            matrixStackIn.translate(move.x+1D, move.y, move.z); //Tried to move 1 block, to see if it works
            //TODO definitely should find a better way for that!
            LOG.info("Moving matrix a bit: {}, owner's motion: {}", move.toString(), owner.getMotion().toString()); //This is what outputs that strange -0.07... motion.
            model.setRotationAngles(entityIn, partialTicks, 0.0F, -0.1F, 0.0F, 0.0F); // TODO describe (does nothing yet, will describe after achieving smooth)
            IVertexBuilder ivertexbuilder = bufferIn.getBuffer(model.getRenderType(this.getEntityTexture(entityIn))); //Copied form boat renderer, as far as I remember...
            model.render(matrixStackIn, ivertexbuilder, packedLightIn, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F,
                    1.0F);

            matrixStackIn.pop();
        } else {
            LOG.info("Owner is null, not rendering");
        }
        super.render(entityIn, entityYaw, partialTicks, matrixStackIn, bufferIn, packedLightIn);
    }

Also, I included raw

this.setPositionAndUpdate(owner.getPosX(), owner.getPosY(), owner.getPosZ());

to entity's tick() method, and I'm sure it's executed on both client and server (Both Server and Render thread are logging message).

By entity in the world start jumping a bit along player's moving direction, as you start to move. Jump distances are proportional to player's velocity. Obviously, this indicates low position update rate, because while using 20FPS render everything works as I'd like to.

 

Would be graceful for any help about all that, thank you.

Edited by Dzuchun
No, entitys position is not updated 20 times per second, only once per 3 ticks.

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

2 hours ago, poopoodice said:

From my experience the motion of player is (0.0, -0.0784000015258789, 0.0) when still is because there's gravitational acceleration

Yep, I think so too, and now I tried to get speed with:

Vector3d ownerSpeed = new Vector3d(owner_.getPosX() - owner_.lastTickPosX,
                                   owner_.getPosY() - owner_.lastTickPosY,
                                   owner_.getPosZ() - owner_.lastTickPosZ);

and looks like it works correctly. So i determined move vector as:

move = ownerSpeed.scale(partialTicks);

And translated matrixStack with it:

matrixStackIn.translate(move.x, move.y, move.z);

(Of course, I make pushes and pops)

But entity still renders as hell, am I missing something?

 

UPDATE:

I realized, that ownerSpeed vector actually determines player's movement during current partialTicks time. So I removed scale method and assigned directly move=ownerSpeed. Result is a bit better now.

Edited by Dzuchun
Described further actions

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

I'm definitely doing something wrong, because even setting constant motion to entity (gravitation was asked to be disabled both on client and server) results jumps with a little shaking.

WHAT SHOULD BE DONE, TO MOVE ENTITY SMOOTH??

(I'm really desprate)

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

16 hours ago, Dzuchun said:

Right to the question: how minecraft achieves smooth entity rendering? As far as I know and experimented with Logging of entity's position in render method, it is updated, like, 20 times per second, which equals to 1 tick. So it seems like while rendering, minecraft uses entity's speed to determine where it should be, or what?

With entity speed (or how much it moved during last tick) and interpolation.

Interpolation is multiplying the difference in distance in that tick by the partial ticks of that frame, and add that to the previous position to achieve smooth movement.

  • Thanks 1

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

8 hours ago, poopoodice said:

Are you using render player event to render your own thing? The event is called whenever the player is being rendered hence you can have same update rate as the player.

Previous time I've tried to make this mod, I tried to start with this. the problem is, this event is not called, if you're using 1st perspective view (default one). But I want for player to see entity from any perspective.
Of cource, I solved this problem with overriding one more event - something about GUI. But this requires twice as much render methods, and separate matrixStack transformations - GUI matrix at start is bound to camera pos and rotation, when RenderPlayerEvent just to PlayerEntity's position. But yeah, that rendered without any shaking and so on.

4 hours ago, DavidM said:

With entity speed (or how much it moved during last tick) and interpolation.

How would I get it? Should I store last tick pos separately, because It looks like Entity.lastTickPos- variables store position at begining of current tick. Actually, I don't really get how Entity.prevPos- and Entity.lastTickPos- differ - as I can see, the only place they are reassigned is Entity#forceSetPosition(double x, double y, double z), and here is reassignment:

this.prevPosX = x;
this.prevPosY = y;
this.prevPosZ = z;
this.lastTickPosX = x;
this.lastTickPosY = y;
this.lastTickPosZ = z;

...

4 hours ago, DavidM said:

Interpolation is multiplying the difference in distance in that tick by the partial ticks of that frame, and add that to the previous position to achieve smooth movement.

Isn't it what I've done here?:

21 hours ago, Dzuchun said:

// Vector3d move = owner.getMotion().scale(partialTicks);

 

Or again, should I calculate speed by myself? How would I do that? Also, using owner's motion gets less jumping than using it's own motion, I suspect no matter what it's motion is always 0, despite I'm calling

MyEntity.setMotion(owner.getMotion());

every tick on both client and server.

 

EDIT:
And where can I see minecraft doing this things? None of renderers are handling that, they're just using matrixStack to render what they need, not even calling entity#getMotion(). Should I use some event?

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

Now I achieved the end of line: every tick on server, entity sends a custom packet to all tracking clients. Clientside entity instance has separate variables for storing previous tick position and real position change during last tick, for me to use them in rendering. And move vector looks like this:

Vector3d move = entityIn.getRealMotion().scale(partialTicks).add(entityIn.getRealPos().add(entityIn.getPositionVec().scale(-1)));

So, I calculate position change due to moving for last partialTiks (getRealMotion() returns REAL motion I calculated with sending packets) AND add to it difference between my entity position and wherever minecraft thinks entity is. This means, that after matrixStackIn.translate(move.x, move.y, move.z) I am at my coordinates (that were certainly unchanged during last tick) + offset from MY personal calculated speed (that couldn't be changed too).

And in the game, like, 70% of the time it looks flawless, but then entity starts jumping over the movement direction.

Looks like minecraft changes coordinates of the entity during rendering, so even with such a complicated code you can't adapt for it.

 

NOTE: No, I can't use Entity.lastTickPos- variables, because they are updated, like, 6-7 times per second, or once in 3 ticks.

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

10 hours ago, poopoodice said:

Another idea is to see how parrots sit on the player's shoulders.

Nope, same render and tick methods as everywhere, except handling dancing near jukebox :D

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

11 hours ago, poopoodice said:

You may find this interesting

The problem is, position on client is updated not every tick, but once at ~3 ticks and this update is kinda laggy - breaks any algorythm I create.

Here is how to prove once-at-3-ticks update:

if (prevPos != entityIn.getPositionVec()) {
    Long currentTime = System.currentTimeMillis();
    LOG.info("Render after {}ms at new position", currentTime-prevPosTime);
    prevPosTime = currentTime;
    prevPos = entityIn.getPositionVec();
}

(placed in render method; prevPos and prevPosTime are fields; outputs ~150ms, which is ~3 ticks)

So with suggested code in the article you will get a total hell - 3 times entity jumps back at one position, and this 6-7 times per second.

What's interesting, same applies to PlayerEntity. That's why I am curious, why smooth render in minecraft is even possible.

 

21 hours ago, Dzuchun said:

Now I achieved the end of line

Actually, here I have approximately 6 unexpected jumps per second.

 

Let me remind: my entity extends Entity class directly, so I checked BoatEntity class and renderer before writing all that.

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

1 hour ago, Dzuchun said:

Nope, same render and tick methods as everywhere, except handling dancing near jukebox :D

Oh yeah, they are actually a part of layers.

Or is it possible instead of getting the position of the player when render, sync the position of custom entity with the owner on server then just render it normally?

Link to comment
Share on other sites

21 minutes ago, diesieben07 said:

Position is synced to the client very 3 ticks (usually, depends on the entity, this is called "update frequency").

You need to update the position on server and client every tick. The client needs to do the same calculations. Then the update every 3 ticks only ensures any slight deviation is corrected.

Sounds logical, I think I got the idea, and according to it I've updated my code. Now:

Every tick, both on client and server, tick() method of my entity executes folowing:

if (hasOwner()) {
	setPositionAndUpdate(owner.getPosX(), owner.getPosY(), owner.getPosZ());
	setMotion(owner.getPosX()-owner.lastTickPosX, owner.getPosY()-owner.lastTickPosY, owner.getPosZ()-owner.lastTickPosZ);
  	LOG.info("Setting motion {}", getMotion());
}

I am sure, that hasOwner() returns true, and code is executed.

Every frame, at render method I execute folowing:

matrixStackIn.push();
matrixStackIn.translate(entityIn.getMotion().x*partialTicks, entityIn.getMotion().y*partialTicks, entityIn.getMotion().z*partialTicks);
model.setRotationAngles(entityIn, partialTicks, 0.0F, -0.1F, 0.0F, 0.0F); // TODO describe (does nothing yet)
IVertexBuilder ivertexbuilder = bufferIn.getBuffer(model.getRenderType(this.getEntityTexture(entityIn)));
model.render(matrixStackIn, ivertexbuilder, packedLightIn, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F);
matrixStackIn.pop();
super.render(entityIn, entityYaw, partialTicks, matrixStackIn, bufferIn, packedLightIn);

But entity jumps like hell, now with any framerate setting. I'm sure that this render method is used - if change it, entity render changes as well.

Super method is Entity#render, as I can see, It renders nameplate (as I can see), executing it before matrixStackIn.pop() changes nothing (tested).

 

So, how exactly should I

53 minutes ago, diesieben07 said:

update the position on server and client every tick.

? Am I doing it correctly?

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

3 minutes ago, diesieben07 said:

Why are you translating by motion? That doesn't make any sense.

You also set the motion of your entity to the position of it's owner... Meaning if the owner is at [10, 30, 10] your entity will try to move (travel) 10 blocks x, 30 blocks up, 10 blocks z in the next tick.

 

10 minutes ago, Dzuchun said:

setMotion(owner.getPosX()-owner.lastTickPosX, owner.getPosY()-owner.lastTickPosY, owner.getPosZ()-owner.lastTickPosZ);

 I'm sorry, but I can see here a setting of motion to a difference of two owner's positions.

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

2 minutes ago, diesieben07 said:

I guess I read that wrong, but that still does not make much sense to me.

What are you trying to achieve by that?

I'm trying to calculate owner's speed. getMotion() won't work because:

 

On 7/14/2020 at 11:32 AM, Dzuchun said:

The problem is, while standing still, player's getMotion().toString() returns (0.0, -0.0784000015258789, 0.0).

Probably, I should use other fields? Should I store something manually?

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

1 minute ago, diesieben07 said:

Why do you want the owner's speed? What is the effect you want to achieve?

I want to assign the same speed to my entity, to achieve is being rendered as if it is glued to a player. I would like to see it in any perspective, so I can't just render it at RenderPlayerEvent.

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

On 7/14/2020 at 11:32 AM, Dzuchun said:

The problem is, while standing still, player's getMotion().toString() returns (0.0, -0.0784000015258789, 0.0)

I can't: while standing still, entity falls underground and then jumps back. I've tried to check if player is collided vertically and if so, add this constant vector to result, but that didn't work - Entity.collidedVertically seemed to be false at any time.

Edited by Dzuchun
comas are importaint!

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

Code:

if (owner.collidedVertically) {
  setMotion(owner.getMotion().add(MOTION_CONSTANT_1));
} else {
  setMotion(owner.getMotion());
}	
LOG.info("Set motion to entity {}, owner's motion: {}", getMotion(), owner.getMotion());

Output:

Quote

[m[32m[14:11:11] [Render thread/INFO] [dz.wi.en.mi.WingsEntity/]: Set motion to entity (-0.9237963696043886, 0.0, -0.07976295232791968), owner's motion: (-0.9237963696043886, 0.0, -0.07976295232791968)
[m[32m[14:11:11] [Server thread/INFO] [dz.wi.en.mi.WingsEntity/]: Set motion to entity (0.0, 0.0, 0.0), owner's motion: (0.0, 0.0, 0.0)

Me:

HMMMMMMMMM, Player's motion on server is 0? I swear, no changes were applied by my mod. Should I update owner field, probably? Like, every tick get owner by it's UUID?

So that's why with my personal update message sent every tick, entity once in 3 ticks got teleported back.

Edited by Dzuchun

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

I just realized, that it is impossible to guarantee, that my entity ticks after it's owner, that's why I get it jumping.

Is it possible, somehow, to guarantee that entity ticks after another entity?

 

Everything said above may be absolutely wrong. No rights reserved.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.