Added human player model

- Extracted NPedModel out of QuadripedModel
  - Both are now configurable
- Added HumanoidModel
- Added human model
- Changed NPedModel animation logic
- Added a few skins including first sketch of Pyotr by WarDreg
- Decreased walking speed
This commit is contained in:
OLEGSHA 2020-11-15 20:58:17 +03:00
parent 38a8a885c8
commit f2e28161a8
12 changed files with 627 additions and 199 deletions

View File

@ -122,4 +122,13 @@ public class LambdaModel extends DynamicModel {
} }
public static LambdaModel animate(Renderable model, TransformGetter transform) {
return new LambdaModel(
new Renderable[] { model },
new Mat4[] { new Mat4() },
new boolean[] { true },
new TransformGetter[] { transform }
);
}
} }

View File

@ -19,10 +19,10 @@ public class ComplexTexture {
this.primitive = primitive; this.primitive = primitive;
this.assumedWidth = abstractWidth this.assumedWidth = abstractWidth
* primitive.getWidth() / (float) primitive.getBufferWidth(); / (float) primitive.getWidth() * primitive.getBufferWidth();
this.assumedHeight = abstractHeight this.assumedHeight = abstractHeight
* primitive.getHeight() / (float) primitive.getBufferHeight(); / (float) primitive.getHeight() * primitive.getBufferHeight();
} }
public Texture get(int x, int y, int width, int height) { public Texture get(int x, int y, int width, int height) {

View File

@ -0,0 +1,116 @@
package ru.windcorp.progressia.client.world.entity;
import static java.lang.Math.*;
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.common.world.entity.EntityData;
public class HumanoidModel extends NPedModel {
protected static abstract class Limb extends BodyPart {
private final float animationOffset;
public Limb(
Renderable renderable, Vec3 joint,
float animationOffset
) {
super(renderable, joint);
this.animationOffset = animationOffset;
}
@Override
protected void applyTransform(Mat4 mat, NPedModel model) {
float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
float value = sin(phase);
float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter();
mat.rotateY(value * amplitude);
}
protected abstract float getSwingAmplitude(HumanoidModel model);
}
public static class Leg extends Limb {
public Leg(
Renderable renderable, Vec3 joint,
float animationOffset
) {
super(renderable, joint, animationOffset);
}
@Override
protected float getSwingAmplitude(HumanoidModel model) {
return model.walkingLegSwing;
}
}
public static class Arm extends Limb {
public Arm(
Renderable renderable, Vec3 joint,
float animationOffset
) {
super(renderable, joint, animationOffset);
}
@Override
protected float getSwingAmplitude(HumanoidModel model) {
return model.walkingArmSwing;
}
}
private final Arm leftArm;
private final Arm rightArm;
private final Leg leftLeg;
private final Leg rightLeg;
private float walkingLegSwing;
private float walkingArmSwing;
public HumanoidModel(
EntityData entity,
Body body, Head head,
Arm leftArm, Arm rightArm,
Leg leftLeg, Leg rightLeg,
float scale
) {
super(entity, body, head, scale);
this.leftArm = leftArm;
this.rightArm = rightArm;
this.leftLeg = leftLeg;
this.rightLeg = rightLeg;
}
@Override
protected void renderBodyParts(ShapeRenderHelper renderer) {
super.renderBodyParts(renderer);
leftArm.render(renderer, this);
rightArm.render(renderer, this);
leftLeg.render(renderer, this);
rightLeg.render(renderer, this);
}
public float getWalkingArmSwing() {
return walkingArmSwing;
}
public float getWalkingLegSwing() {
return walkingLegSwing;
}
public HumanoidModel setWalkingLegSwing(float walkingLegSwing) {
this.walkingLegSwing = walkingLegSwing;
return this;
}
public HumanoidModel setWalkingArmSwing(float walkingArmSwing) {
this.walkingArmSwing = walkingArmSwing;
return this;
}
}

View File

@ -0,0 +1,284 @@
package ru.windcorp.progressia.client.world.entity;
import static java.lang.Math.atan2;
import static java.lang.Math.min;
import static java.lang.Math.pow;
import static java.lang.Math.toRadians;
import static ru.windcorp.progressia.common.util.FloatMathUtils.normalizeAngle;
import glm.Glm;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData;
public abstract class NPedModel extends EntityRenderable {
protected static abstract class BodyPart {
private final Renderable renderable;
private final Vec3 translation = new Vec3();
public BodyPart(Renderable renderable, Vec3 joint) {
this.renderable = renderable;
if (joint != null) {
this.translation.set(joint);
}
}
protected void render(
ShapeRenderHelper renderer, NPedModel model
) {
renderer.pushTransform().translate(translation);
applyTransform(renderer.pushTransform(), model);
renderable.render(renderer);
renderer.popTransform();
renderer.popTransform();
}
protected abstract void applyTransform(Mat4 mat, NPedModel model);
public Vec3 getTranslation() {
return translation;
}
}
public static class Body extends BodyPart {
public Body(Renderable renderable) {
super(renderable, null);
}
@Override
protected void applyTransform(Mat4 mat, NPedModel model) {
// Do nothing
}
}
public static class Head extends BodyPart {
private final float maxYaw;
private final float maxPitch;
private final Vec3 viewPoint;
public Head(
Renderable renderable, Vec3 joint,
double maxYawDegrees, double maxPitchDegrees,
Vec3 viewPoint
) {
super(renderable, joint);
this.maxYaw = (float) toRadians(maxYawDegrees);
this.maxPitch = (float) toRadians(maxPitchDegrees);
this.viewPoint = viewPoint;
}
@Override
protected void applyTransform(Mat4 mat, NPedModel model) {
mat.rotateZ(model.getHeadYaw()).rotateY(model.getHeadPitch());
}
public Vec3 getViewPoint() {
return viewPoint;
}
}
protected final Body body;
protected final Head head;
private float walkingParameter = 0;
private float velocityParameter = 0;
private float velocity = 0;
/**
* If {@link #velocity} is greater than this value, {@link #velocityParameter} is 1.0.
*/
private float maxEffectiveVelocity = 5 * Units.METERS_PER_SECOND;
/**
* If {@link #velocity} is less than {@link #maxEffectiveVelocity}, then
* {@code velocityCoeff = exp(velocity / maxEffectiveVelocity, velocityCoeffPower)}.
*/
private float velocityCoeffPower = 1;
private final float scale;
private float walkingFrequency;
private float bodyYaw = Float.NaN;
private float headYaw;
private float headPitch;
public NPedModel(EntityData data, Body body, Head head, float scale) {
super(data);
this.body = body;
this.head = head;
this.scale = scale;
}
@Override
public void render(ShapeRenderHelper renderer) {
renderer.pushTransform().scale(scale).rotateZ(bodyYaw);
renderBodyParts(renderer);
renderer.popTransform();
accountForVelocity();
evaluateAngles();
}
protected void renderBodyParts(ShapeRenderHelper renderer) {
body.render(renderer, this);
head.render(renderer, this);
}
private void evaluateAngles() {
float globalYaw = normalizeAngle(getData().getYaw());
if (Float.isNaN(bodyYaw)) {
bodyYaw = globalYaw;
headYaw = 0;
} else {
headYaw = normalizeAngle(globalYaw - bodyYaw);
if (headYaw > +head.maxYaw) {
bodyYaw += headYaw - +head.maxYaw;
headYaw = +head.maxYaw;
} else if (headYaw < -head.maxYaw) {
bodyYaw += headYaw - -head.maxYaw;
headYaw = -head.maxYaw;
}
}
bodyYaw = normalizeAngle(bodyYaw);
headPitch = Glm.clamp(
getData().getPitch(),
-head.maxPitch, head.maxPitch
);
}
private void accountForVelocity() {
Vec3 horizontal = Vectors.grab3();
horizontal.set(getData().getVelocity());
horizontal.z = 0;
velocity = horizontal.length();
evaluateVelocityCoeff();
// TODO switch to world time
walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
bodyYaw += velocityParameter * normalizeAngle(
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
) * min(1, GraphicsInterface.getFrameLength() * 10);
Vectors.release(horizontal);
}
private void evaluateVelocityCoeff() {
if (velocity > maxEffectiveVelocity) {
velocityParameter = 1;
} else {
velocityParameter = (float) pow(velocity / maxEffectiveVelocity, velocityCoeffPower);
}
}
@Override
public void getViewPoint(Vec3 output) {
Mat4 m = Matrices.grab4();
Vec4 v = Vectors.grab4();
m.identity()
.scale(scale)
.rotateZ(bodyYaw)
.translate(head.getTranslation())
.rotateZ(headYaw)
.rotateY(headPitch);
v.set(head.getViewPoint(), 1);
m.mul(v);
output.set(v.x, v.y, v.z);
Vectors.release(v);
Matrices.release(m);
}
public Body getBody() {
return body;
}
public Head getHead() {
return head;
}
public float getBodyYaw() {
return bodyYaw;
}
public float getHeadYaw() {
return headYaw;
}
public float getHeadPitch() {
return headPitch;
}
/**
* Returns a number in the range [0; 1] that can be used to scale animation effects that depend on speed.
* This parameter is 0 when the entity is not moving and 1 when it's moving "fast".
* @return velocity parameter
*/
protected float getVelocityParameter() {
return velocityParameter;
}
/**
* Returns a number that can be used to parameterize animation effects that depend on walking.
* This parameter increases when the entity moves (e.g. this can be total traveled distance).
* @return walking parameter
*/
protected float getWalkingParameter() {
return walkingParameter;
}
protected float getVelocity() {
return velocity;
}
public float getScale() {
return scale;
}
protected float getWalkingFrequency() {
return walkingFrequency;
}
public NPedModel setWalkingFrequency(float walkingFrequency) {
this.walkingFrequency = walkingFrequency;
return this;
}
public float getMaxEffectiveVelocity() {
return maxEffectiveVelocity;
}
public float getVelocityCoeffPower() {
return velocityCoeffPower;
}
public NPedModel setMaxEffectiveVelocity(float maxEffectiveVelocity) {
this.maxEffectiveVelocity = maxEffectiveVelocity;
return this;
}
public NPedModel setVelocityCoeffPower(float velocityCoeffPower) {
this.velocityCoeffPower = velocityCoeffPower;
return this;
}
}

View File

@ -2,86 +2,15 @@ package ru.windcorp.progressia.client.world.entity;
import static java.lang.Math.*; import static java.lang.Math.*;
import static ru.windcorp.progressia.common.util.FloatMathUtils.*; import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
import static ru.windcorp.progressia.common.util.FloatMathUtils.sin;
import glm.Glm;
import glm.mat._4.Mat4; import glm.mat._4.Mat4;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
public class QuadripedModel extends EntityRenderable { public class QuadripedModel extends NPedModel {
private static abstract class BodyPart {
private final Renderable renderable;
private final Vec3 translation = new Vec3();
public BodyPart(Renderable renderable, Vec3 joint) {
this.renderable = renderable;
if (joint != null) {
this.translation.set(joint);
}
}
protected void render(
ShapeRenderHelper renderer, QuadripedModel model
) {
renderer.pushTransform().translate(translation);
applyTransform(renderer.pushTransform(), model);
renderable.render(renderer);
renderer.popTransform();
renderer.popTransform();
}
protected abstract void applyTransform(Mat4 mat, QuadripedModel model);
public Vec3 getTranslation() {
return translation;
}
}
public static class Body extends BodyPart {
public Body(Renderable renderable) {
super(renderable, null);
}
@Override
protected void applyTransform(Mat4 mat, QuadripedModel model) {
// Do nothing
}
}
public static class Head extends BodyPart {
private final float maxYaw;
private final float maxPitch;
private final Vec3 viewPoint;
public Head(
Renderable renderable, Vec3 joint,
double maxYawDegrees, double maxPitchDegrees,
Vec3 viewPoint
) {
super(renderable, joint);
this.maxYaw = (float) toRadians(maxYawDegrees);
this.maxPitch = (float) toRadians(maxPitchDegrees);
this.viewPoint = viewPoint;
}
@Override
protected void applyTransform(Mat4 mat, QuadripedModel model) {
mat.rotateZ(model.headYaw).rotateY(model.headPitch);
}
public Vec3 getViewPoint() {
return viewPoint;
}
}
public static class Leg extends BodyPart { public static class Leg extends BodyPart {
private final float animationOffset; private final float animationOffset;
@ -95,34 +24,20 @@ public class QuadripedModel extends EntityRenderable {
} }
@Override @Override
protected void applyTransform(Mat4 mat, QuadripedModel model) { protected void applyTransform(Mat4 mat, NPedModel model) {
mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
float value = sin(phase);
float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter();
mat.rotateY(value * amplitude);
} }
} }
private final Body body;
private final Head head;
private final Leg leftForeLeg, rightForeLeg; private final Leg leftForeLeg, rightForeLeg;
private final Leg leftHindLeg, rightHindLeg; private final Leg leftHindLeg, rightHindLeg;
private final float scale;
private float walkingAnimationParameter = 0;
private float velocityCoeff = 0;
private float velocity = 0;
/**
* Controls how quickly velocityCoeff approaches 1
*/
private float velocityCutoff = 10;
private float walkingFrequency = 0.15f / 60.0f;
private float walkingSwing = (float) toRadians(30); private float walkingSwing = (float) toRadians(30);
private float bodyYaw = Float.NaN;
private float headYaw;
private float headPitch;
public QuadripedModel( public QuadripedModel(
EntityData entity, EntityData entity,
@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable {
float scale float scale
) { ) {
super(entity); super(entity, body, head, scale);
this.body = body;
this.head = head;
this.leftForeLeg = leftForeLeg; this.leftForeLeg = leftForeLeg;
this.rightForeLeg = rightForeLeg; this.rightForeLeg = rightForeLeg;
this.leftHindLeg = leftHindLeg; this.leftHindLeg = leftHindLeg;
this.rightHindLeg = rightHindLeg; this.rightHindLeg = rightHindLeg;
this.scale = scale;
} }
@Override @Override
public void render(ShapeRenderHelper renderer) { protected void renderBodyParts(ShapeRenderHelper renderer) {
renderer.pushTransform().scale(scale).rotateZ(bodyYaw); super.renderBodyParts(renderer);
body.render(renderer, this); this.leftForeLeg.render(renderer, this);
head.render(renderer, this); this.rightForeLeg.render(renderer, this);
leftForeLeg.render(renderer, this); this.leftHindLeg.render(renderer, this);
rightForeLeg.render(renderer, this); this.rightHindLeg.render(renderer, this);
leftHindLeg.render(renderer, this);
rightHindLeg.render(renderer, this);
renderer.popTransform();
accountForVelocity();
evaluateAngles();
} }
private void evaluateAngles() { public float getWalkingSwing() {
float globalYaw = normalizeAngle(getData().getYaw()); return walkingSwing;
if (Float.isNaN(bodyYaw)) {
bodyYaw = globalYaw;
headYaw = 0;
} else {
headYaw = normalizeAngle(globalYaw - bodyYaw);
if (headYaw > +head.maxYaw) {
bodyYaw += headYaw - +head.maxYaw;
headYaw = +head.maxYaw;
} else if (headYaw < -head.maxYaw) {
bodyYaw += headYaw - -head.maxYaw;
headYaw = -head.maxYaw;
}
}
bodyYaw = normalizeAngle(bodyYaw);
headPitch = Glm.clamp(
getData().getPitch(),
-head.maxPitch, head.maxPitch
);
} }
private void accountForVelocity() { public QuadripedModel setWalkingSwing(float walkingSwing) {
Vec3 horizontal = Vectors.grab3(); this.walkingSwing = walkingSwing;
horizontal.set(getData().getVelocity()); return this;
horizontal.z = 0;
velocity = horizontal.length();
evaluateVelocityCoeff();
// TODO switch to world time
walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
bodyYaw += velocityCoeff * normalizeAngle(
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
) * min(1, GraphicsInterface.getFrameLength() * 10);
Vectors.release(horizontal);
}
private void evaluateVelocityCoeff() {
if (velocity * velocityCutoff > 1) {
velocityCoeff = 1;
} else {
velocityCoeff = velocity * velocityCutoff;
velocityCoeff *= velocityCoeff;
}
}
@Override
public void getViewPoint(Vec3 output) {
Mat4 m = Matrices.grab4();
Vec4 v = Vectors.grab4();
m.identity()
.scale(scale)
.rotateZ(bodyYaw)
.translate(head.getTranslation())
.rotateZ(headYaw)
.rotateY(headPitch);
v.set(head.getViewPoint(), 1);
m.mul(v);
output.set(v.x, v.y, v.z);
Vectors.release(v);
Matrices.release(m);
} }
} }

View File

@ -80,7 +80,7 @@ public class ChunkData {
TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers");
TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); TileData sand = TileDataRegistry.getInstance().get("Test:Sand");
Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2); Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2).sub(getPosition());
Vec3i pos = new Vec3i(); Vec3i pos = new Vec3i();
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
@ -132,18 +132,28 @@ public class ChunkData {
} }
} }
EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); if (!getPosition().any()) {
javapony.setEntityId(0x42); // EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
javapony.setPosition(new Vec3(-6, -6, 20)); // javapony.setEntityId(0x42);
javapony.setDirection(new Vec2( // javapony.setPosition(new Vec3(-6, -6, 20));
(float) Math.toRadians(40), (float) Math.toRadians(45) // javapony.setDirection(new Vec2(
)); // (float) Math.toRadians(40), (float) Math.toRadians(45)
getEntities().add(javapony); // ));
// getEntities().add(javapony);
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
statie.setEntityId(0xDEADBEEF); player.setEntityId(0x42);
statie.setPosition(new Vec3(0, 15, 16)); player.setPosition(new Vec3(-6, -6, 20));
getEntities().add(statie); player.setDirection(new Vec2(
(float) Math.toRadians(40), (float) Math.toRadians(45)
));
getEntities().add(player);
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
statie.setEntityId(0xDEADBEEF);
statie.setPosition(new Vec3(0, 15, 16));
getEntities().add(statie);
}
} }
public BlockData getBlock(Vec3i posInChunk) { public BlockData getBlock(Vec3i posInChunk) {

View File

@ -87,9 +87,14 @@ public class TestContent {
} }
private static void registerEntities() { private static void registerEntities() {
registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); // registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
register(new TestEntityRenderJavapony()); // register(new TestEntityRenderJavapony());
register(new EntityLogic("Test", "Javapony")); // register(new EntityLogic("Test", "Javapony"));
float scale = 1.8f / 8;
registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f)));
register(new TestEntityRenderHuman());
register(new EntityLogic("Test", "Player"));
register("Test", "Statie", TestEntityDataStatie::new); register("Test", "Statie", TestEntityDataStatie::new);
register(new TestEntityRenderStatie()); register(new TestEntityRenderStatie());

View File

@ -0,0 +1,164 @@
package ru.windcorp.progressia.test;
import static java.lang.Math.toRadians;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.LambdaModel;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder;
import ru.windcorp.progressia.client.graphics.model.StaticModel;
import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.entity.HumanoidModel;
import ru.windcorp.progressia.client.world.entity.EntityRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.world.entity.EntityData;
import static java.lang.Math.*;
public class TestEntityRenderHuman extends EntityRender {
private static final float SECOND_LAYER_OFFSET = 1 / 12f;
private final Renderable body;
private final Renderable head;
private final Renderable leftArm;
private final Renderable rightArm;
private final Renderable leftLeg;
private final Renderable rightLeg;
public TestEntityRenderHuman() {
super("Test", "Player");
ComplexTexture texture = new ComplexTexture(
EntityRenderRegistry.getEntityTexture("pyotr"),
16, 16
);
this.body = createBody(texture);
this.head = createHead(texture);
this.leftArm = createLimb(texture, 8, 0, 12, 0, true, true);
this.rightArm = createLimb(texture, 10, 8, 10, 4, true, false);
this.leftLeg = createLimb(texture, 4, 0, 0, 0, false, true);
this.rightLeg = createLimb(texture, 0, 8, 0, 4, false, false);
}
private Renderable createBody(ComplexTexture texture) {
return createLayeredCuboid(
texture,
4, 8,
4, 4,
2, 3, 1,
-0.5f, -1, 3,
1, 2, 3
);
}
private Renderable createHead(ComplexTexture texture) {
return createLayeredCuboid(
texture,
0, 12,
8, 12,
2, 2, 2,
-1, -1, 0,
2, 2, 2
);
}
private Renderable createLimb(
ComplexTexture texture,
int tx, int ty,
int tx2, int ty2,
boolean isArm, boolean isLeft
) {
Renderable model = createLayeredCuboid(
texture,
tx, ty,
tx2, ty2,
1, 3, 1,
-0.5f, -0.5f, isArm ? -2.5f : -3f,
1, 1, 3
);
if (isArm) {
return LambdaModel.animate(
model,
mat -> {
double phase = GraphicsInterface.getTime() + (isLeft ? 0 : Math.PI / 3);
mat.rotateX((isLeft ? +1 : -1) * 1/40f * (sin(phase) + 1));
mat.rotateY(1/20f * sin(Math.PI / 3 * phase));
}
);
} else {
return model;
}
}
private Renderable createLayeredCuboid(
ComplexTexture texture,
int tx, int ty,
int tx2, int ty2,
int tw, int th, int td,
float ox, float oy, float oz,
float sx, float sy, float sz
) {
WorldRenderProgram program = WorldRenderProgram.getDefault();
StaticModel.Builder b = StaticModel.builder();
// First layer
b.addPart(new PppBuilder(
program,
texture.getCuboidTextures(tx, ty, tw, th, td)
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create());
ox -= SECOND_LAYER_OFFSET;
oy -= SECOND_LAYER_OFFSET;
oz -= SECOND_LAYER_OFFSET;
sx += SECOND_LAYER_OFFSET * 2;
sy += SECOND_LAYER_OFFSET * 2;
sz += SECOND_LAYER_OFFSET * 2;
// Second layer
b.addPart(new PppBuilder(
program,
texture.getCuboidTextures(tx2, ty2, tw, th, td)
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create());
return new StaticModel(b);
}
@Override
public EntityRenderable createRenderable(EntityData entity) {
return new HumanoidModel(
entity,
new HumanoidModel.Body(body),
new HumanoidModel.Head(
head, new Vec3(0, 0, 6), 70, 25, new Vec3(1.2f, 0, 1.5f)
),
new HumanoidModel.Arm(
leftArm, new Vec3(0, +1.5f, 3 + 3 - 0.5f), 0.0f
),
new HumanoidModel.Arm(
rightArm, new Vec3(0, -1.5f, 3 + 3 - 0.5f), FloatMathUtils.PI_F
),
new HumanoidModel.Leg(
leftLeg, new Vec3(0, +0.5f, 3), FloatMathUtils.PI_F
),
new HumanoidModel.Leg(
rightLeg, new Vec3(0, -0.5f, 3), 0.0f
),
1.8f / (3 + 3 + 2)
)
.setWalkingArmSwing((float) toRadians(30))
.setWalkingLegSwing((float) toRadians(50))
.setWalkingFrequency(0.15f / 60.0f);
}
}

View File

@ -39,7 +39,7 @@ public class TestPlayerControls {
private static final float FLYING_CONTROL_AUTHORITY = 0.05f; private static final float FLYING_CONTROL_AUTHORITY = 0.05f;
// Horizontal and vertical max control speed when walking // Horizontal and vertical max control speed when walking
private static final float WALKING_SPEED = 5.0f * Units.METERS_PER_SECOND; private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority // (0; 1], 1 is instant change, 0 is no control authority
private static final float WALKING_CONTROL_AUTHORITY = 0.1f; private static final float WALKING_CONTROL_AUTHORITY = 0.1f;

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB