Skip to content

Commit

Permalink
Added head rotation to player model
Browse files Browse the repository at this point in the history
  • Loading branch information
Martomate committed Dec 26, 2024
1 parent 3ee2027 commit faefff3
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 31 deletions.
4 changes: 3 additions & 1 deletion client/src/main/scala/hexacraft/client/ClientWorld.scala
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ class ClientWorld(val worldInfo: WorldInfo) extends BlockRepository with BlocksI
e.motion.velocity.set(v)
case EntityEvent.Flying(f) =>
e.motion.flying = f
case EntityEvent.HeadDirection(d) =>
e.headDirection.foreach(_.direction = d)
}
case None =>
event match {
Expand Down Expand Up @@ -344,7 +346,7 @@ class ClientWorld(val worldInfo: WorldInfo) extends BlockRepository with BlocksI
val vel = e.motion.velocity
val horizontalSpeedSq = vel.x * vel.x + vel.z * vel.z
if e.model.isDefined then {
e.model.get.tick(horizontalSpeedSq > 0.1)
e.model.get.tick(horizontalSpeedSq > 0.1, e.headDirection.map(_.direction))
}
}

Expand Down
13 changes: 7 additions & 6 deletions client/src/main/scala/hexacraft/client/GameClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,13 @@ class GameClient(
val entityEventData = entityEventsNbt.getList("events").get.map(_.asMap.get)
val entityEvents = for (id, eventNbt) <- entityEventIds.zip(entityEventData) yield {
val event = eventNbt.getString("type").get match {
case "spawned" => EntityEvent.Spawned(eventNbt.getMap("data").get)
case "despawned" => EntityEvent.Despawned
case "position" => EntityEvent.Position(CylCoords(eventNbt.getMap("pos").get.setVector(new Vector3d)))
case "rotation" => EntityEvent.Rotation(eventNbt.getMap("r").get.setVector(new Vector3d))
case "velocity" => EntityEvent.Velocity(eventNbt.getMap("v").get.setVector(new Vector3d))
case "flying" => EntityEvent.Flying(eventNbt.getBoolean("f", false))
case "spawned" => EntityEvent.Spawned(eventNbt.getMap("data").get)
case "despawned" => EntityEvent.Despawned
case "position" => EntityEvent.Position(CylCoords(eventNbt.getMap("pos").get.setVector(new Vector3d)))
case "rotation" => EntityEvent.Rotation(eventNbt.getMap("r").get.setVector(new Vector3d))
case "velocity" => EntityEvent.Velocity(eventNbt.getMap("v").get.setVector(new Vector3d))
case "flying" => EntityEvent.Flying(eventNbt.getBoolean("f", false))
case "head_direction" => EntityEvent.HeadDirection(eventNbt.getMap("d").get.setVector(new Vector3d))
}

(UUID.fromString(id), event)
Expand Down
1 change: 1 addition & 0 deletions game/src/main/scala/hexacraft/world/EntityEvent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ enum EntityEvent {
case Rotation(v: Vector3d)
case Velocity(v: Vector3d)
case Flying(f: Boolean)
case HeadDirection(d: Vector3d)
}
5 changes: 5 additions & 0 deletions game/src/main/scala/hexacraft/world/entity/base.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class Entity(val id: UUID, val typeName: String, private val components: Seq[Ent
.map(_.asInstanceOf[MotionComponent])
.orNull

val headDirection: Option[HeadDirectionComponent] = components
.find(_.isInstanceOf[HeadDirectionComponent])
.map(_.asInstanceOf[HeadDirectionComponent])

val boundingBox: HexBox = components
.find(_.isInstanceOf[BoundsComponent])
.map(_.asInstanceOf[BoundsComponent].bounds)
Expand Down Expand Up @@ -72,6 +76,7 @@ object EntityFactory {
val components = Seq(
TransformComponent.fromNBT(tag),
MotionComponent.fromNBT(tag),
HeadDirectionComponent.fromNBT(tag),
BoundsComponent(playerBounds),
ModelComponent(PlayerEntityModel.create("player"))
)
Expand Down
12 changes: 12 additions & 0 deletions game/src/main/scala/hexacraft/world/entity/components.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ object MotionComponent {
}
}

@deprecated("a better solution needs to be made soon")
class HeadDirectionComponent(var direction: Vector3d = new Vector3d) extends EntityComponent

object HeadDirectionComponent {
def fromNBT(tag: Nbt.MapTag)(using CylinderSize): HeadDirectionComponent = {
val dir = new Vector3d
tag.getMap("head_direction").foreach(_.setVector(dir))

HeadDirectionComponent(dir)
}
}

class ModelComponent(val model: EntityModel) extends EntityComponent

class AiComponent(val ai: EntityAI) extends EntityComponent
Expand Down
4 changes: 2 additions & 2 deletions game/src/main/scala/hexacraft/world/entity/models.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package hexacraft.world.entity
import hexacraft.world.HexBox
import hexacraft.world.coord.CylCoords

import org.joml.{Matrix4f, Vector3f}
import org.joml.{Matrix4f, Vector3d, Vector3f}

trait EntityModel {
def parts: Seq[EntityPart]
def textureName: String
def tick(walking: Boolean): Unit
def tick(walking: Boolean, headDirection: Option[Vector3d]): Unit
}

trait EntityPart {
Expand Down
21 changes: 15 additions & 6 deletions game/src/main/scala/hexacraft/world/entity/player.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package hexacraft.world.entity
import hexacraft.world.{CylinderSize, HexBox}
import hexacraft.world.coord.BlockCoords

import org.joml.Vector3f
import org.joml.{Vector3d, Vector3f}

class PlayerEntityModel(
val headBase: BasicEntityPart, // not rendered
val head: BasicEntityPart,
val leftBodyHalf: BasicEntityPart,
val rightBodyHalf: BasicEntityPart,
Expand All @@ -19,15 +20,15 @@ class PlayerEntityModel(

private val animation = new PlayerAnimation(this)

override def tick(walking: Boolean): Unit = {
animation.tick(walking)
override def tick(walking: Boolean, headDirection: Option[Vector3d]): Unit = {
animation.tick(walking, headDirection.getOrElse(new Vector3d))
}
}

class PlayerAnimation(model: PlayerEntityModel) {
private var time = 0

def tick(walking: Boolean): Unit = {
def tick(walking: Boolean, headDirection: Vector3d): Unit = {
if walking || time % 30 != 0 then {
time += 1
}
Expand All @@ -39,6 +40,8 @@ class PlayerAnimation(model: PlayerEntityModel) {

model.rightLeg.rotation.z = 0.5f * math.sin(phase).toFloat
model.leftLeg.rotation.z = -0.5f * math.sin(phase).toFloat

model.headBase.rotation.z = -headDirection.x.toFloat
}
}

Expand Down Expand Up @@ -66,7 +69,9 @@ object PlayerEntityModel {
val armBounds = makeHexBox(armRadius, -armRadius * CylinderSize.y60.toFloat, armLength)
val legBounds = makeHexBox(legRadius, 0, legLength)

val headY = bodyLength + legLength + headRadius * CylinderSize.y60
val headBaseY = bodyLength + legLength
val headY = headRadius * CylinderSize.y60
val headBasePos = makePartPosition(0, headBaseY, 0).toCylCoordsOffset
val headPos = makePartPosition(0, headY, 0).toCylCoordsOffset

val rightBodyPos = makePartPosition(0, legLength, 0.5 * bodyRadius).toCylCoordsOffset
Expand All @@ -81,8 +86,12 @@ object PlayerEntityModel {

val pi = math.Pi.toFloat

val headBase = BasicEntityPart(HexBox(0, 0, 0), headBasePos, Vector3f())
val head = BasicEntityPart(headBounds, headPos, Vector3f(0, pi / 2, pi / 2), (0, 176), headBase)

PlayerEntityModel(
head = BasicEntityPart(headBounds, headPos, Vector3f(0, pi / 2, pi / 2), (0, 176)),
headBase = headBase,
head = head,
leftBodyHalf = BasicEntityPart(bodyBounds, leftBodyPos, Vector3f(0, 0, 0), (0, 120)),
rightBodyHalf = BasicEntityPart(bodyBounds, rightBodyPos, Vector3f(0, 0, 0), (48, 120)),
rightArm = BasicEntityPart(armBounds, rightArmPos, Vector3f(pi, 0, 0), (48, 64)),
Expand Down
4 changes: 2 additions & 2 deletions game/src/main/scala/hexacraft/world/entity/sheep.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package hexacraft.world.entity
import hexacraft.world.{CylinderSize, HexBox}
import hexacraft.world.coord.BlockCoords

import org.joml.Vector3f
import org.joml.{Vector3d, Vector3f}

class SheepEntityModel(
val head: BasicEntityPart,
Expand All @@ -18,7 +18,7 @@ class SheepEntityModel(

private val animation = new SheepAnimation(this)

override def tick(walking: Boolean): Unit = {
override def tick(walking: Boolean, headDirection: Option[Vector3d]): Unit = {
animation.tick(walking)
}
}
Expand Down
33 changes: 20 additions & 13 deletions server/src/main/scala/hexacraft/server/GameServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import hexacraft.world.coord.*
import hexacraft.world.entity.*

import com.martomate.nbt.Nbt
import org.joml.Vector2f
import org.joml.{Vector2f, Vector3d}
import org.zeromq.ZMQException

import java.util.UUID
Expand Down Expand Up @@ -149,6 +149,7 @@ class GameServer(
entity.transform.rotation.set(0, math.Pi * 0.5 - player.rotation.y, 0)
entity.motion.velocity.set(player.velocity)
entity.motion.flying = player.flying
entity.headDirection.foreach(_.direction.set(player.rotation.x, 0, 0))
}

val tickResult = chunksLoadedPerPlayer.synchronized {
Expand Down Expand Up @@ -184,6 +185,9 @@ class GameServer(
p.entityEventsWaitingToBeSent += p2.entity.id -> EntityEvent.Rotation(p2.entity.transform.rotation)
p.entityEventsWaitingToBeSent += p2.entity.id -> EntityEvent.Velocity(p2.entity.motion.velocity)
p.entityEventsWaitingToBeSent += p2.entity.id -> EntityEvent.Flying(p2.entity.motion.flying)
p2.entity.headDirection.foreach { comp =>
p.entityEventsWaitingToBeSent += p2.entity.id -> EntityEvent.HeadDirection(comp.direction)
}
}
}
}
Expand Down Expand Up @@ -385,6 +389,7 @@ class GameServer(
Seq(
TransformComponent(CylCoords(player.position).offset(-2, -2, -1)),
MotionComponent(),
HeadDirectionComponent(Vector3d(player.rotation.x, 0, 0)),
BoundsComponent(EntityFactory.playerBounds)
)
)
Expand Down Expand Up @@ -484,21 +489,23 @@ class GameServer(

val events = for (_, e) <- entityEvents yield {
val name = e match {
case EntityEvent.Spawned(_) => "spawned"
case EntityEvent.Despawned => "despawned"
case EntityEvent.Position(_) => "position"
case EntityEvent.Rotation(_) => "rotation"
case EntityEvent.Velocity(_) => "velocity"
case EntityEvent.Flying(_) => "flying"
case EntityEvent.Spawned(_) => "spawned"
case EntityEvent.Despawned => "despawned"
case EntityEvent.Position(_) => "position"
case EntityEvent.Rotation(_) => "rotation"
case EntityEvent.Velocity(_) => "velocity"
case EntityEvent.Flying(_) => "flying"
case EntityEvent.HeadDirection(_) => "head_direction"
}

val extraFields: Seq[(String, Nbt)] = e match {
case EntityEvent.Spawned(data) => Seq("data" -> data)
case EntityEvent.Despawned => Seq()
case EntityEvent.Position(pos) => Seq("pos" -> Nbt.makeVectorTag(pos.toVector3d))
case EntityEvent.Rotation(r) => Seq("r" -> Nbt.makeVectorTag(r))
case EntityEvent.Velocity(v) => Seq("v" -> Nbt.makeVectorTag(v))
case EntityEvent.Flying(f) => Seq("f" -> Nbt.ByteTag(f))
case EntityEvent.Spawned(data) => Seq("data" -> data)
case EntityEvent.Despawned => Seq()
case EntityEvent.Position(pos) => Seq("pos" -> Nbt.makeVectorTag(pos.toVector3d))
case EntityEvent.Rotation(r) => Seq("r" -> Nbt.makeVectorTag(r))
case EntityEvent.Velocity(v) => Seq("v" -> Nbt.makeVectorTag(v))
case EntityEvent.Flying(f) => Seq("f" -> Nbt.ByteTag(f))
case EntityEvent.HeadDirection(d) => Seq("d" -> Nbt.makeVectorTag(d))
}

Nbt.makeMap(extraFields*).withField("type", Nbt.StringTag(name))
Expand Down
2 changes: 1 addition & 1 deletion server/src/main/scala/hexacraft/server/ServerWorld.scala
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ class ServerWorld(worldProvider: WorldProvider, val worldInfo: WorldInfo)
entityPhysicsSystem.update(e.transform, e.motion, e.boundingBox)

if e.model.isDefined then {
e.model.get.tick(e.motion.velocity.lengthSquared() > 0.1)
e.model.get.tick(e.motion.velocity.lengthSquared() > 0.1, e.headDirection.map(_.direction))
}
}

Expand Down

0 comments on commit faefff3

Please sign in to comment.