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:
parent
38a8a885c8
commit
f2e28161a8
@ -121,5 +121,14 @@ 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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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,33 +24,19 @@ 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() {
|
|
||||||
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
|
|
||||||
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() {
|
public float getWalkingSwing() {
|
||||||
if (velocity * velocityCutoff > 1) {
|
return walkingSwing;
|
||||||
velocityCoeff = 1;
|
|
||||||
} else {
|
|
||||||
velocityCoeff = velocity * velocityCutoff;
|
|
||||||
velocityCoeff *= velocityCoeff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public QuadripedModel setWalkingSwing(float walkingSwing) {
|
||||||
public void getViewPoint(Vec3 output) {
|
this.walkingSwing = walkingSwing;
|
||||||
Mat4 m = Matrices.grab4();
|
return this;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
|
||||||
statie.setEntityId(0xDEADBEEF);
|
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||||
statie.setPosition(new Vec3(0, 15, 16));
|
player.setEntityId(0x42);
|
||||||
getEntities().add(statie);
|
player.setPosition(new Vec3(-6, -6, 20));
|
||||||
|
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) {
|
||||||
|
@ -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());
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
BIN
src/main/resources/assets/textures/entities/pyotr.png
Normal file
BIN
src/main/resources/assets/textures/entities/pyotr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
BIN
src/main/resources/assets/textures/entities/test_skin.png
Normal file
BIN
src/main/resources/assets/textures/entities/test_skin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/assets/textures/entities/test_skin_layout.png
Normal file
BIN
src/main/resources/assets/textures/entities/test_skin_layout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Reference in New Issue
Block a user