diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java index c6799c5..00d47b1 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java @@ -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 } + ); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java index d8f4c18..8aaf4d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java @@ -19,10 +19,10 @@ public class ComplexTexture { this.primitive = primitive; this.assumedWidth = abstractWidth - * primitive.getWidth() / (float) primitive.getBufferWidth(); + / (float) primitive.getWidth() * primitive.getBufferWidth(); this.assumedHeight = abstractHeight - * primitive.getHeight() / (float) primitive.getBufferHeight(); + / (float) primitive.getHeight() * primitive.getBufferHeight(); } public Texture get(int x, int y, int width, int height) { diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java new file mode 100644 index 0000000..45817db --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java @@ -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; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java new file mode 100644 index 0000000..441ff6e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java @@ -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; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java index c19a3b0..00311d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java @@ -2,86 +2,15 @@ package ru.windcorp.progressia.client.world.entity; import static java.lang.Math.*; 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.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.util.Matrices; -import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.entity.EntityData; -public class QuadripedModel extends EntityRenderable { - - 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 class QuadripedModel extends NPedModel { public static class Leg extends BodyPart { private final float animationOffset; @@ -95,33 +24,19 @@ public class QuadripedModel extends EntityRenderable { } @Override - protected void applyTransform(Mat4 mat, QuadripedModel model) { - mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); + protected void applyTransform(Mat4 mat, NPedModel model) { + 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 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 bodyYaw = Float.NaN; - private float headYaw; - private float headPitch; public QuadripedModel( EntityData entity, @@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable { float scale ) { - super(entity); + super(entity, body, head, scale); - this.body = body; - this.head = head; this.leftForeLeg = leftForeLeg; this.rightForeLeg = rightForeLeg; this.leftHindLeg = leftHindLeg; this.rightHindLeg = rightHindLeg; - - this.scale = scale; } @Override - public void render(ShapeRenderHelper renderer) { - renderer.pushTransform().scale(scale).rotateZ(bodyYaw); - body.render(renderer, this); - head.render(renderer, this); - leftForeLeg.render(renderer, this); - rightForeLeg.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); + protected void renderBodyParts(ShapeRenderHelper renderer) { + super.renderBodyParts(renderer); + this.leftForeLeg.render(renderer, this); + this.rightForeLeg.render(renderer, this); + this.leftHindLeg.render(renderer, this); + this.rightHindLeg.render(renderer, this); } - private void evaluateVelocityCoeff() { - if (velocity * velocityCutoff > 1) { - velocityCoeff = 1; - } else { - velocityCoeff = velocity * velocityCutoff; - velocityCoeff *= velocityCoeff; - } + public float getWalkingSwing() { + return walkingSwing; } - @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 QuadripedModel setWalkingSwing(float walkingSwing) { + this.walkingSwing = walkingSwing; + return this; } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 18d44b0..f25ac0a 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -80,7 +80,7 @@ public class ChunkData { TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); 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(); for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { @@ -132,18 +132,28 @@ public class ChunkData { } } - EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); - javapony.setEntityId(0x42); - javapony.setPosition(new Vec3(-6, -6, 20)); - javapony.setDirection(new Vec2( - (float) Math.toRadians(40), (float) Math.toRadians(45) - )); - getEntities().add(javapony); - - EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); - statie.setEntityId(0xDEADBEEF); - statie.setPosition(new Vec3(0, 15, 16)); - getEntities().add(statie); + if (!getPosition().any()) { +// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); +// javapony.setEntityId(0x42); +// javapony.setPosition(new Vec3(-6, -6, 20)); +// javapony.setDirection(new Vec2( +// (float) Math.toRadians(40), (float) Math.toRadians(45) +// )); +// getEntities().add(javapony); + + EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); + player.setEntityId(0x42); + 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) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 7b24efa..1b2d16e 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -87,9 +87,14 @@ public class TestContent { } private static void registerEntities() { - registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); - register(new TestEntityRenderJavapony()); - register(new EntityLogic("Test", "Javapony")); +// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); +// register(new TestEntityRenderJavapony()); +// 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(new TestEntityRenderStatie()); diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java new file mode 100644 index 0000000..cecfacf --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java @@ -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); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index a41a3c2..5ad784b 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -39,7 +39,7 @@ public class TestPlayerControls { private static final float FLYING_CONTROL_AUTHORITY = 0.05f; // 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 private static final float WALKING_CONTROL_AUTHORITY = 0.1f; diff --git a/src/main/resources/assets/textures/entities/pyotr.png b/src/main/resources/assets/textures/entities/pyotr.png new file mode 100644 index 0000000..a0cff20 Binary files /dev/null and b/src/main/resources/assets/textures/entities/pyotr.png differ diff --git a/src/main/resources/assets/textures/entities/test_skin.png b/src/main/resources/assets/textures/entities/test_skin.png new file mode 100644 index 0000000..e75b5e8 Binary files /dev/null and b/src/main/resources/assets/textures/entities/test_skin.png differ diff --git a/src/main/resources/assets/textures/entities/test_skin_layout.png b/src/main/resources/assets/textures/entities/test_skin_layout.png new file mode 100644 index 0000000..4a3d0a1 Binary files /dev/null and b/src/main/resources/assets/textures/entities/test_skin_layout.png differ