Skip to content

Commit

Permalink
Add SpriteUtil
Browse files Browse the repository at this point in the history
- Ripped out of comments from EnemyChanger
Fix CharmHelper Linux Crash
- Fixes #3
Only initialize Menu and Title modules by default
Up version number
  • Loading branch information
SFGrenade committed Jan 10, 2022
1 parent d9fe9df commit ad7cd87
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 9 deletions.
26 changes: 23 additions & 3 deletions CharmHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,26 @@ public static class CharmHelper
/// List of sprites to use for the charms.
/// </summary>
private static readonly List<Sprite> CustomSprites = new List<Sprite>();


/// <summary>
/// A detour for a private method that has no body.
/// </summary>
private static MonoMod.RuntimeDetour.Detour BuildEquippedCharms_Start_hook;

/// <summary>
/// Constructs the mod and hooks important functions.
/// </summary>
static CharmHelper()
{
On.PlayerData.CalculateNotchesUsed += OnPlayerDataCalculateNotchesUsed;
On.BuildEquippedCharms.Start += OnBuildEquippedCharmsStart;
// i hate this, can't have shit in detroid
//On.BuildEquippedCharms.Start += OnBuildEquippedCharmsStart;
// workaround, since above isn't working
BuildEquippedCharms_Start_hook = new MonoMod.RuntimeDetour.Detour(typeof(BuildEquippedCharms).GetMethod("Start", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic), typeof(CharmHelper).GetMethod(nameof(OnBuildEquippedCharmsStart_single), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic));
BuildEquippedCharms_Start_hook.Apply();
On.GameManager.Start += OnGameManagerStart;
}

/// <summary>
/// Used for static initialization.
/// </summary>
Expand Down Expand Up @@ -420,7 +430,17 @@ private static void InitBuildEquippedCharms(BuildEquippedCharms self)

self.gameObjectList = tmplist;
}


/// <summary>
/// On hook to initialize charms and equipped charms.
/// </summary>
private static void OnBuildEquippedCharmsStart_single()
{
Init();

InitBuildEquippedCharms(GameObject.FindObjectOfType<BuildEquippedCharms>(true));
}

/// <summary>
/// On hook to initialize charms and equipped charms.
/// </summary>
Expand Down
9 changes: 5 additions & 4 deletions SFCore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using System.IO;
using System.Reflection;
using Modding;
using SFCore.Utils;

Expand All @@ -12,9 +13,9 @@ public class SFCoreMod : Mod
static SFCoreMod()
{
// to load all components
SFCore.AchievementHelper.unusedInit();
SFCore.CharmHelper.unusedInit();
SFCore.EnviromentParticleHelper.unusedInit();
//SFCore.AchievementHelper.unusedInit();
//SFCore.CharmHelper.unusedInit();
//SFCore.EnviromentParticleHelper.unusedInit();
//SFCore.ItemHelper.unusedInit();
SFCore.MenuStyleHelper.unusedInit();
SFCore.TitleLogoHelper.unusedInit();
Expand Down
4 changes: 2 additions & 2 deletions SFCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<Product>SFCore</Product>
<Description>A Hollow Knight Mod Library</Description>
<Copyright>Copyright © SFGrenade 2019-2021</Copyright>
<AssemblyVersion>1.5.2.1</AssemblyVersion>
<FileVersion>1.5.2.1</FileVersion>
<AssemblyVersion>1.5.2.2</AssemblyVersion>
<FileVersion>1.5.2.2</FileVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<LangVersion>latest</LangVersion>
<NoWarn>8002</NoWarn>
Expand Down
136 changes: 136 additions & 0 deletions Util/SpriteUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UObject = UnityEngine.Object;

namespace SFCore.Utils
{
/// <summary>
/// Utils specifically for Sprites and Textures.
/// </summary>
public static class SpriteUtil
{
private delegate Color Texture2D_GetPixelImpl(int image, int x, int y);
private static MethodInfo tex_GetPixelImpl = typeof(Texture2D).GetMethod("GetPixelImpl", BindingFlags.NonPublic | BindingFlags.Instance);

private static Texture2D_GetPixelImpl getGetPixel(Texture2D tex)
{
return (Texture2D_GetPixelImpl) tex_GetPixelImpl.CreateDelegate(typeof(Texture2D_GetPixelImpl), tex);
}

/// <summary>
/// Calculates the area between 3 integer points.
/// </summary>
/// <param name="a">A point of the triangle</param>
/// <param name="b">A point of the triangle</param>
/// <param name="c">A point of the triangle</param>
/// <returns>The area of the triangle.</returns>
public static float CalcTriangleArea(Vector2Int a, Vector2Int b, Vector2Int c)
{
return Mathf.Abs(((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) / 2f);
}

/// <summary>
/// Returns a new Texture2D that is cropped.
/// </summary>
/// <param name="tex">The texture to crop</param>
/// <param name="x">Left offset</param>
/// <param name="y">Top offset</param>
/// <param name="width">Width of the new texture</param>
/// <param name="height">Height of the new texture</param>
/// <returns>The cropped Texture2D.</returns>
public static Texture2D GetReadableCroppedTexture(Texture2D tex, int x, int y, int width, int height)
{
float xScale = (float)width / tex.width;
float yScale = (float)height / tex.height;
float xOffset = (float)x / tex.width;
float yOffset = (float)y / tex.height;
Texture2D ret = new Texture2D(width, height);
RenderTexture tempRt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
Graphics.Blit(tex, tempRt, new Vector2(xScale, yScale), new Vector2(xOffset, yOffset));
RenderTexture tmpActiveRt = RenderTexture.active;
RenderTexture.active = tempRt;
ret.ReadPixels(new Rect(0, 0, ret.width, ret.height), 0, 0);
ret.Apply();
RenderTexture.active = tmpActiveRt;
RenderTexture.ReleaseTemporary(tempRt);
return ret;
}

/// <summary>
/// Extracts a sprite from a texture, returns a new texture which is cropped to only the sprite according to its UV values.
/// </summary>
/// <param name="sprite">The sprite to extract</param>
/// <returns>A texture with just the sprite on it.</returns>
public static Texture2D ExtractTextureFromSprite(Sprite sprite)
{
var spriteRect = (sprite.texture.width, sprite.texture.height);
List<Vector2Int> texUVs = new List<Vector2Int>();
List<(Vector2Int, Vector2Int, Vector2Int)> tmpTriangles = new List<(Vector2Int, Vector2Int, Vector2Int)>();
int i;
bool[][] contents;
float triangleArea;
float pab, pbc, pac;
Vector2Int p;
int x, y;
int minX, maxX, minY, maxY;
int width, height;
Texture2D outTex;

foreach (var item in sprite.uv)
{
texUVs.Add(new Vector2Int(Mathf.RoundToInt(item.x * (spriteRect.width - 1)), Mathf.RoundToInt(item.y * (spriteRect.height - 1))));
}
for (i = 0; i < sprite.triangles.Length; i += 3)
{
tmpTriangles.Add((texUVs[sprite.triangles[i]], texUVs[sprite.triangles[i + 1]], texUVs[sprite.triangles[i + 2]]));
}

minX = texUVs.Min(uv => uv.x);
maxX = texUVs.Max(uv => uv.x);
minY = texUVs.Min(uv => uv.y);
maxY = texUVs.Max(uv => uv.y);
width = maxX - minX + 1;
height = maxY - minY + 1;

contents = new bool[height][];
for (i = 0; i < contents.Length; i++)
contents[i] = new bool[width];
foreach (var item in tmpTriangles)
{
triangleArea = CalcTriangleArea(item.Item1, item.Item2, item.Item3);
for (x = 0; x < width; x++)
{
for (y = 0; y < height; y++)
{
p = new Vector2Int(minX + x, minY + y);
pab = CalcTriangleArea(item.Item1, item.Item2, p);
pbc = CalcTriangleArea(p, item.Item2, item.Item3);
pac = CalcTriangleArea(item.Item1, p, item.Item3);
if ((pab + pbc + pac) == triangleArea)
{
contents[y][x] = true;
}
}
}

}

outTex = GetReadableCroppedTexture(sprite.texture, minX, minY, width, height);

for (x = 0; x < width; x++)
{
for (y = 0; y < height; y++)
{
if (!contents[y][x])
outTex.SetPixel(x, y, new Color(0, 0, 0, 0));
}
}
outTex.Apply();

return outTex;
}
}
}

0 comments on commit ad7cd87

Please sign in to comment.