summary refs log tree commit diff
path: root/src/HeldBlockRenderer.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/HeldBlockRenderer.c
initial commit
Diffstat (limited to 'src/HeldBlockRenderer.c')
-rw-r--r--src/HeldBlockRenderer.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/HeldBlockRenderer.c b/src/HeldBlockRenderer.c
new file mode 100644
index 0000000..93511c3
--- /dev/null
+++ b/src/HeldBlockRenderer.c
@@ -0,0 +1,267 @@
+#include "HeldBlockRenderer.h"
+#include "Block.h"
+#include "Game.h"
+#include "Inventory.h"
+#include "Graphics.h"
+#include "Camera.h"
+#include "ExtMath.h"
+#include "Event.h"
+#include "Entity.h"
+#include "Model.h"
+#include "Options.h"
+
+cc_bool HeldBlockRenderer_Show;
+static BlockID held_block;
+static struct Entity held_entity;
+static struct Matrix held_blockProj;
+
+static cc_bool held_animating, held_breaking, held_swinging;
+static float held_swingY;
+static float held_time, held_period = 0.25f;
+static BlockID held_lastBlock;
+
+/* Since not using Entity_SetModel, which normally automatically does this */
+static void SetHeldModel(struct Model* model) {
+#ifdef CC_BUILD_CONSOLE
+	static int maxVertices;
+	if (model->maxVertices <= maxVertices) return;
+
+	maxVertices = model->maxVertices;
+	Gfx_DeleteDynamicVb(&held_entity.ModelVB);
+#endif
+}
+
+static void HeldBlockRenderer_RenderModel(void) {
+	struct Model* model;
+
+	Gfx_SetFaceCulling(true);
+	Gfx_SetDepthTest(false);
+	/* Gfx_SetDepthWrite(false); */
+	/* TODO: Need to properly reallocate per model VB here */
+
+	if (Blocks.Draw[held_block] == DRAW_GAS) {
+		model = Entities.CurPlayer->Base.Model;
+		SetHeldModel(model);
+		Vec3_Set(held_entity.ModelScale, 1.0f,1.0f,1.0f);
+
+		Model_RenderArm(model, &held_entity);
+		Gfx_SetAlphaTest(false);
+	} else {	
+		model = Models.Block;
+		SetHeldModel(model);
+		Vec3_Set(held_entity.ModelScale, 0.4f,0.4f,0.4f);
+
+		Gfx_SetupAlphaState(Blocks.Draw[held_block]);
+		Model_Render(model, &held_entity);
+		Gfx_RestoreAlphaState(Blocks.Draw[held_block]);
+	}
+	
+	Gfx_SetDepthTest(true);
+	/* Gfx_SetDepthWrite(true); */
+	Gfx_SetFaceCulling(false);
+}
+
+static void SetMatrix(void) {
+	struct Entity* p = &Entities.CurPlayer->Base;
+	struct Matrix lookAt;
+	Vec3 eye = { 0,0,0 }; eye.y = Entity_GetEyeHeight(p);
+
+	Matrix_Translate(&lookAt, -eye.x, -eye.y, -eye.z);
+	Matrix_Mul(&Gfx.View, &lookAt, &Camera.TiltM);
+}
+
+static void ResetHeldState(void) {
+	/* Based off details from http://pastebin.com/KFV0HkmD (Thanks goodlyay!) */
+	struct Entity* p = &Entities.CurPlayer->Base;
+	Vec3 eye = { 0,0,0 }; eye.y = Entity_GetEyeHeight(p);
+	held_entity.Position = eye;
+
+	held_entity.Position.x -= Camera.BobbingHor;
+	held_entity.Position.y -= Camera.BobbingVer;
+	held_entity.Position.z -= Camera.BobbingHor;
+
+	held_entity.Yaw   = -45.0f; held_entity.RotY = -45.0f;
+	held_entity.Pitch = 0.0f;   held_entity.RotX = 0.0f;
+	held_entity.ModelBlock   = held_block;
+
+	held_entity.SkinType     = p->SkinType;
+	held_entity.TextureId    = p->TextureId;
+	held_entity.MobTextureId = p->MobTextureId;
+	held_entity.uScale       = p->uScale;
+	held_entity.vScale       = p->vScale;
+}
+
+static void SetBaseOffset(void) {
+	cc_bool sprite = Blocks.Draw[held_block] == DRAW_SPRITE;
+	Vec3 normalOffset = { 0.56f, -0.72f, -0.72f };
+	Vec3 spriteOffset = { 0.46f, -0.52f, -0.72f };
+	Vec3 offset = sprite ? spriteOffset : normalOffset;
+
+	Vec3_AddBy(&held_entity.Position, &offset);
+	if (!sprite && Blocks.Draw[held_block] != DRAW_GAS) {
+		float height = Blocks.MaxBB[held_block].y - Blocks.MinBB[held_block].y;
+		held_entity.Position.y += 0.2f * (1.0f - height);
+	}
+}
+
+static void OnProjectionChanged(void* obj) {
+	float fov = 70.0f * MATH_DEG2RAD;
+	float aspectRatio = (float)Game.Width / (float)Game.Height;
+	Gfx_CalcPerspectiveMatrix(&held_blockProj, fov, aspectRatio, (float)Game_ViewDistance);
+}
+
+/* Based off incredible gifs from (Thanks goodlyay!)
+	https://dl.dropboxusercontent.com/s/iuazpmpnr89zdgb/slowBreakTranslate.gif
+	https://dl.dropboxusercontent.com/s/z7z8bset914s0ij/slowBreakRotate1.gif
+	https://dl.dropboxusercontent.com/s/pdq79gkzntquld1/slowBreakRotate2.gif
+	https://dl.dropboxusercontent.com/s/w1ego7cy7e5nrk1/slowBreakFull.gif
+
+	https://github.com/UnknownShadow200/ClassicalSharp/wiki/Dig-animation-details
+*/
+static void HeldBlockRenderer_DigAnimation(void) {
+	float sinHalfCircle, sinHalfCircleWeird;
+	float t, sqrtLerpPI;
+
+	t = held_time / held_period;
+	sinHalfCircle = Math_SinF(t * MATH_PI);
+	sqrtLerpPI    = Math_SqrtF(t) * MATH_PI;
+
+	held_entity.Position.x -= Math_SinF(sqrtLerpPI)     * 0.4f;
+	held_entity.Position.y += Math_SinF(sqrtLerpPI * 2) * 0.2f;
+	held_entity.Position.z -= sinHalfCircle            * 0.2f;
+
+	sinHalfCircleWeird = Math_SinF(t * t * MATH_PI);
+	held_entity.RotY  -= Math_SinF(sqrtLerpPI) * 80.0f;
+	held_entity.Yaw   -= Math_SinF(sqrtLerpPI) * 80.0f;
+	held_entity.RotX  += sinHalfCircleWeird    * 20.0f;
+}
+
+static void HeldBlockRenderer_ResetAnim(cc_bool setLastHeld, float period) {
+	held_time = 0.0f; held_swingY = 0.0f;
+	held_animating = false; held_swinging = false;
+	held_period = period;
+	if (setLastHeld) { held_lastBlock = Inventory_SelectedBlock; }
+}
+
+static PackedCol HeldBlockRenderer_GetCol(struct Entity* entity) {
+	struct Entity* player;
+	PackedCol col;
+	float adjPitch, t, scale;
+
+	player = &Entities.CurPlayer->Base;
+	col    = player->VTABLE->GetCol(player);
+
+	/* Adjust pitch so angle when looking straight down is 0. */
+	adjPitch = player->Pitch - 90.0f;
+	if (adjPitch < 0.0f) adjPitch += 360.0f;
+
+	/* Adjust color so held block is brighter when looking straight up */
+	t     = Math_AbsF(adjPitch - 180.0f) / 180.0f;
+	scale = Math_Lerp(0.9f, 0.7f, t);
+	return PackedCol_Scale(col, scale);
+}
+
+void HeldBlockRenderer_ClickAnim(cc_bool digging) {
+	/* TODO: timing still not quite right, rotate2 still not quite right */
+	HeldBlockRenderer_ResetAnim(true, digging ? 0.35 : 0.25);
+	held_swinging  = false;
+	held_breaking  = digging;
+	held_animating = true;
+	/* Start place animation at bottom of cycle */
+	if (!digging) held_time = held_period / 2;
+}
+
+static void DoSwitchBlockAnim(void* obj) {
+	if (held_swinging) {
+		/* Like graph -sin(x) : x=0.5 and x=2.5 have same y values,
+		   but increasing x causes y to change in opposite directions */
+		if (held_time > held_period * 0.5f) {
+			held_time = held_period - held_time;
+		}
+	} else {
+		if (held_block == Inventory_SelectedBlock) return;
+		HeldBlockRenderer_ResetAnim(false, 0.25);
+		held_animating = true;
+		held_swinging = true;
+	}
+}
+
+static void OnBlockChanged(void* obj, IVec3 coords, BlockID old, BlockID now) {
+	if (now == BLOCK_AIR) return;
+	HeldBlockRenderer_ClickAnim(false);
+}
+
+static void DoAnimation(float delta, float lastSwingY) {
+	float t;
+	if (!held_animating) return;
+
+	if (held_swinging || !held_breaking) {
+		t = held_time / held_period;
+		held_swingY = -0.4f * Math_SinF(t * MATH_PI);
+		held_entity.Position.y += held_swingY;
+
+		if (held_swinging) {
+			/* i.e. the block has gone to bottom of screen and is now returning back up. 
+			   At this point we switch over to the new held block. */
+			if (held_swingY > lastSwingY) held_lastBlock = held_block;
+			held_block = held_lastBlock;
+			held_entity.ModelBlock = held_block;
+		}
+	} else {
+		HeldBlockRenderer_DigAnimation();
+	}
+	
+	held_time += delta;
+	if (held_time > held_period) {
+		HeldBlockRenderer_ResetAnim(true, 0.25f);
+	}
+}
+
+void HeldBlockRenderer_Render(float delta) {
+	float lastSwingY;
+	struct Matrix view;
+	if (!HeldBlockRenderer_Show) return;
+
+	lastSwingY  = held_swingY;
+	held_swingY = 0.0f;
+	held_block  = Inventory_SelectedBlock;
+	view = Gfx.View;
+
+	Gfx_LoadMatrix(MATRIX_PROJECTION, &held_blockProj);
+	SetMatrix();
+
+	ResetHeldState();
+	DoAnimation(delta, lastSwingY);
+	SetBaseOffset();
+	if (!Camera.Active->isThirdPerson) HeldBlockRenderer_RenderModel();
+
+	Gfx.View = view;
+	Gfx_LoadMatrix(MATRIX_PROJECTION, &Gfx.Projection);
+}
+
+
+static void OnContextLost(void* obj) {
+	Gfx_DeleteDynamicVb(&held_entity.ModelVB);
+}
+
+static const struct EntityVTABLE heldEntity_VTABLE = {
+	NULL, NULL, NULL, HeldBlockRenderer_GetCol,
+	NULL, NULL
+};
+static void OnInit(void) {
+	Entity_Init(&held_entity);
+	held_entity.VTABLE  = &heldEntity_VTABLE;
+	held_entity.NoShade = true;
+
+	HeldBlockRenderer_Show = Options_GetBool(OPT_SHOW_BLOCK_IN_HAND, true);
+	held_lastBlock         = Inventory_SelectedBlock;
+
+	Event_Register_(&GfxEvents.ProjectionChanged, NULL, OnProjectionChanged);
+	Event_Register_(&UserEvents.HeldBlockChanged, NULL, DoSwitchBlockAnim);
+	Event_Register_(&UserEvents.BlockChanged,     NULL, OnBlockChanged);
+	Event_Register_(&GfxEvents.ContextLost,       NULL, OnContextLost);
+}
+
+struct IGameComponent HeldBlockRenderer_Component = {
+	OnInit /* Init  */
+};