sizedapparel/source/SizedApparel/SizedApparelBodyPart.cs

1034 lines
38 KiB
C#
Raw Normal View History

2023-04-04 01:47:48 +00:00
using HarmonyLib;
using RimWorld;
using System;
2022-08-14 20:46:19 +00:00
using System.Collections.Generic;
using System.Linq;
2023-04-04 01:47:48 +00:00
using System.Reflection;
2022-08-14 20:46:19 +00:00
//using AlienRace;
using UnityEngine;
2023-04-04 01:47:48 +00:00
using Verse;
2022-08-14 20:46:19 +00:00
namespace SizedApparel
{
2023-04-04 01:47:49 +00:00
public class Depth4Offsets
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:47:49 +00:00
public float south=0;
public float north=0;
public float east=0;
public float west=0;
public Depth4Offsets() { }
public Depth4Offsets(Vector4 arg)
{
south = arg.x;
north = arg.y;
east = arg.z;
west = arg.w;
}
public Depth4Offsets(float s, float n, float e, float w)
{
south = s;
north = n;
east = e;
west = w;
}
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
public class Rot4Offsets
2022-08-14 20:46:19 +00:00
{
//X: right and left
//Y: Frong or Back
//Z: Up and Down
Vector3 South;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
Vector3 North;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
Vector3 East;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
Vector3 West;
public Rot4Offsets(Vector3 vector)
{
South = vector;
North = vector;
East = vector;
West = vector;
}
public Rot4Offsets(Vector3 south, Vector3 north, Vector3 east, Vector3 west)
{
South = south;
North = north;
East = east;
West = west;
}
public Vector3 GetOffset(Rot4 rotation)
{
if (rotation == Rot4.East)
return East;
if (rotation == Rot4.West)
return West;
if (rotation == Rot4.South)
return South;
if (rotation == Rot4.North)
return North;
else
return Vector3.zero;
}
}
public struct RaceNameAndBodyType
{
public string raceName;
public string bodyType;
}
2023-04-04 01:47:49 +00:00
public class BodyWithBodyType
{
public string bodyType;
public List<BodyPart> Addons = new List<BodyPart>();
}
2022-08-14 20:46:19 +00:00
public class BodyPart
{
2023-04-04 01:47:49 +00:00
public string partName = null;
public string customPath = null;
public string defaultHediffName = null; // for missing Hediff
public bool isBreasts = false;
2023-04-04 01:47:50 +00:00
public bool centeredTexture = true;
public bool mustMatchBodyType = false; // TODO
2023-04-04 01:47:49 +00:00
public string boneName = null;
public Bone bone = null; // For Graphic Positioning System
2023-04-04 01:47:50 +00:00
public bool mustHaveBone = true; // when bone is missing, don't draw
2023-04-04 01:47:49 +00:00
public SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf.None;
public ColorType colorType = ColorType.Skin;
public Depth4Offsets depthOffset = new Depth4Offsets();
public BodyTypeAndOffset offsets = new BodyTypeAndOffset();
2022-08-14 20:46:19 +00:00
}
public class BodyTypeAndOffset
{
//public RaceNameAndBodyType bodyTypeData;
public string bodyType;
public Rot4Offsets offsets = new Rot4Offsets(Vector3.zero);
2023-04-04 01:47:49 +00:00
public BodyTypeAndOffset()
{
}
2022-08-14 20:46:19 +00:00
public BodyTypeAndOffset(bool useCenter)
{
if (useCenter)
{
2023-04-04 01:47:49 +00:00
offsets = new Rot4Offsets(Vector3.zero);
2022-08-14 20:46:19 +00:00
}
}
public BodyTypeAndOffset(Vector3 defaultOffset)
{
offsets = new Rot4Offsets(defaultOffset);
}
}
2023-04-04 01:47:49 +00:00
public enum ColorType
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:48:55 +00:00
Skin, Hair, Nipple, Custom, None
2022-08-14 20:46:19 +00:00
}
public enum SizedApparelBodyPartOf
{
2023-04-04 01:48:56 +00:00
All, Torso, Breasts, Nipples, Crotch, Penis, Balls, Vagina, Anus, Belly, PubicHair, Udder, Hips, Thighs, hands, feet, None
2022-08-14 20:46:19 +00:00
}
public static class SizedApparelBodyPartOfExtension
{
public static bool IsPartOf(this SizedApparelBodyPartOf source, SizedApparelBodyPartOf target)
{
if (source == SizedApparelBodyPartOf.None)
return false;
switch (target)
{
case SizedApparelBodyPartOf.All:
return true;
case SizedApparelBodyPartOf.Torso:
if (source == SizedApparelBodyPartOf.hands || source == SizedApparelBodyPartOf.feet)
return false;
return true;
case SizedApparelBodyPartOf.Breasts:
2023-04-04 01:48:56 +00:00
if (source == SizedApparelBodyPartOf.Breasts || source == SizedApparelBodyPartOf.Nipples)
return true;
return false;
case SizedApparelBodyPartOf.Nipples:
if (source == SizedApparelBodyPartOf.Nipples)
2022-08-14 20:46:19 +00:00
return true;
return false;
case SizedApparelBodyPartOf.Crotch:
2023-04-04 01:47:49 +00:00
if (source == SizedApparelBodyPartOf.Crotch || source == SizedApparelBodyPartOf.Penis || source == SizedApparelBodyPartOf.Vagina || source == SizedApparelBodyPartOf.Anus || source == SizedApparelBodyPartOf.PubicHair || source == SizedApparelBodyPartOf.Balls)
2022-08-14 20:46:19 +00:00
return true;
return false;
case SizedApparelBodyPartOf.Penis:
if (source == SizedApparelBodyPartOf.Penis)
return true;
return false;
2023-04-04 01:47:49 +00:00
case SizedApparelBodyPartOf.Balls:
if (source == SizedApparelBodyPartOf.Balls)
return true;
return false;
2022-08-14 20:46:19 +00:00
case SizedApparelBodyPartOf.Vagina:
if (source == SizedApparelBodyPartOf.Vagina)
return true;
return false;
case SizedApparelBodyPartOf.Anus:
if (source == SizedApparelBodyPartOf.Anus)
return true;
return false;
case SizedApparelBodyPartOf.Belly:
if (source == SizedApparelBodyPartOf.Belly)
return true;
return false;
case SizedApparelBodyPartOf.Udder:
if (source == SizedApparelBodyPartOf.Udder)
return true;
return false;
case SizedApparelBodyPartOf.Hips:
if (source == SizedApparelBodyPartOf.Hips || source == SizedApparelBodyPartOf.Thighs || source == SizedApparelBodyPartOf.Penis || source == SizedApparelBodyPartOf.Vagina || source == SizedApparelBodyPartOf.Anus)
return true;
return false;
case SizedApparelBodyPartOf.Thighs:
if (source == SizedApparelBodyPartOf.Thighs)
return true;
return false;
case SizedApparelBodyPartOf.hands:
if (source == SizedApparelBodyPartOf.hands)
return true;
return false;
case SizedApparelBodyPartOf.feet:
if (source == SizedApparelBodyPartOf.feet)
return true;
return false;
2023-04-04 01:47:49 +00:00
case SizedApparelBodyPartOf.PubicHair:
if (source == SizedApparelBodyPartOf.PubicHair)
return true;
return false;
2022-08-14 20:46:19 +00:00
case SizedApparelBodyPartOf.None:
return false;
}
Log.Error("[SizedApparel] missing SizedApparelBodyPartOf!");
return false;
}
}
public class GraphicPointsDef : Def
{
public List<TextureWithGraphicPoints> points;
}
public class TextureWithGraphicPoints
{
public string texturePath; // texture is already classified with bodytype
public List<GraphicPoint> points = new List<GraphicPoint>();
}
public class GraphicPoint
{
public string pointName;
2023-04-04 01:47:49 +00:00
public Vector2 point = Vector2.zero;
2022-08-14 20:46:19 +00:00
}
public class GraphicPointsWithBodyType
{
public string pointName;
public List<PointWithBodyType> points = new List<PointWithBodyType>();
}
public class PointWithBodyType
{
public string bodyTypeName; //null can be used too
2023-04-04 01:47:49 +00:00
public Vector2 point = Vector2.zero;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
public class BodyPartPoint
2022-08-14 20:46:19 +00:00
{
string name;
2023-04-04 01:47:49 +00:00
Vector2 position = Vector2.zero;//Uv position. not pixel
2022-08-14 20:46:19 +00:00
}
[Obsolete]//todo
public struct BodyPartSpline
{
}
2023-04-04 01:47:49 +00:00
2023-04-04 01:47:48 +00:00
//Def for Hediff Graphic color options or else.
public class SizedApparelHeddifDef : Def
{
public HediffDef hediffDef;
//public string hediffDefName;
}
2022-08-14 20:46:19 +00:00
//Def per graphic(texture)
2023-04-04 01:47:49 +00:00
[Obsolete]
2022-08-14 20:46:19 +00:00
public class SizedApparelBodyPartGraphicDef : Def
{
public string graphicPath;
public int severityIndex;
public Vector2 pivot = new Vector2(0.5f, 0.5f); // custom pivot of texture. UV. not pixel
//public Dictionary<string, BodyPartPoint> points = new Dictionary<string, BodyPartPoint>();
//public Dictionary<string, BodyPartSpline> splines = new Dictionary<string, BodyPartSpline>();
}
//Def per BodyParts
public class SizedApparelBodyPartDef : Def
{
SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf.None;
2023-04-04 01:47:49 +00:00
public bool canPose = true;
public List<string> TexturePaths;
2022-08-14 20:46:19 +00:00
}
public class SizedApparelBodyPart
{
static MethodInfo overrideMatMethod = AccessTools.Method(typeof(PawnRenderer), "OverrideMaterialIfNeeded");
2023-04-04 01:48:55 +00:00
static Color defaultNippleColor = Color.white;//nipple texture is already colored with pink. so make it white as default to avoid double coloring pink //Strong Pink Color = new ColorInt(255, 121, 121).ToColor
//this is for RGB Channel Edit
static string texturePath_White = "SizedApparel/Masks/White";
static string texturePath_Black = "SizedApparel/Masks/Black";
static string texturePath_Red = "SizedApparel/Masks/Red";
static string texturePath_Green = "SizedApparel/Masks/Green";
static string texturePath_Blue = "SizedApparel/Masks/Blue";
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:48 +00:00
public bool AutoOffsetForFurCoveredBody = true;
2023-04-04 01:47:50 +00:00
public SizedApparelBodyPart(Pawn pawn, ApparelRecorderComp apparelRecorderComp, string bodyPartName, SizedApparelBodyPartOf bodyPartOf, string defaultHediffName, bool isBreast, bool isOverlay, string customPathName = null, ColorType colorOf = ColorType.Skin, bool needBoneToRender = true, Bone parentBone = null, bool isCenteredTexture = false )
2022-08-14 20:46:19 +00:00
{
this.pawn = pawn; //owner
2023-04-04 01:47:48 +00:00
this.apparelRecorderCompCache = apparelRecorderComp; //for reduce GetComp Call; if it is null, it will try to get pawn's comp.
2022-08-14 20:46:19 +00:00
this.bodyPartName = bodyPartName;
this.def = DefDatabase<SizedApparelBodyPartDef>.AllDefs.FirstOrDefault(b => b.defName == bodyPartName);
this.bodyPartOf = bodyPartOf;
this.defaultHediffName = defaultHediffName;
this.isBreast = isBreast;
this.isOverlay = isOverlay;
this.customPath = customPathName;
this.colorType = colorOf;
2023-04-04 01:47:49 +00:00
this.bone = parentBone;
2023-04-04 01:47:50 +00:00
this.mustHaveBone = needBoneToRender;
2023-04-04 01:47:49 +00:00
this.centeredTexture = isCenteredTexture;
}
public void SetCenteredTexture(bool isCentered)
{
this.centeredTexture = isCentered;
2022-08-14 20:46:19 +00:00
}
public Vector2 OffsetFromUVOffset(Vector2 vector, Mesh mesh , bool isFliped = false)
{
//treat mesh as plane
//Vector3 width = mesh.vertices[2] - mesh.vertices[1];
//Vector3 height = mesh.vertices[1] - mesh.vertices[2];
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
if(!isFliped)
2023-04-04 01:47:49 +00:00
return new Vector2((mesh.vertices[2].x - mesh.vertices[0].x)*vector.x,(mesh.vertices[0].z - mesh.vertices[2].z)*vector.y);
return new Vector2((mesh.vertices[2].x - mesh.vertices[0].x)*vector.x, (mesh.vertices[2].z - mesh.vertices[0].z)*vector.y);
/*
* Vector2 loc = new Vector2(0.5f, 0.5f) - vector;
if(!isFliped)
2022-08-14 20:46:19 +00:00
return new Vector2(Mathf.Lerp(mesh.vertices[0].x, mesh.vertices[2].x, loc.x), Mathf.Lerp(mesh.vertices[0].z, mesh.vertices[2].z, loc.y));
return new Vector2(Mathf.Lerp(mesh.vertices[3].x, mesh.vertices[1].x, loc.x), Mathf.Lerp(mesh.vertices[3].z, mesh.vertices[1].z, loc.y));
2023-04-04 01:47:49 +00:00
*/
2022-08-14 20:46:19 +00:00
}
//public Vector2 OffestFromUVOffset(Vector2 vector, Vector2 drawSize, bool isFliped = false)
public SizedApparelBodyPartDef def;
public Pawn pawn;
2023-04-04 01:47:48 +00:00
public ApparelRecorderComp apparelRecorderCompCache; // for reduce getComp call;
2023-04-04 01:47:49 +00:00
public Bone bone;
2023-04-04 01:47:50 +00:00
private bool mustHaveBone;
2023-04-04 01:47:49 +00:00
public bool centeredTexture = false; // false to keep original position from mesh. and consider this graphics pivot as bone position
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
public string bodyPartName; //breast, penis, belly, pubichair... etc. just name. not like architech something
public string customPath = null;
public SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf.None;
public string defaultHediffName;
public bool isBreast = false;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
public bool isOverlay = false; //write z cache?
public string currentHediffName;
public bool isVisible = true;
2023-04-04 01:47:48 +00:00
public int lastPoseTick = -1;
2023-04-04 01:47:49 +00:00
public ColorType colorType = ColorType.Skin;
2022-08-14 20:46:19 +00:00
public Color? customColorOne;
public Color? customColorTwo;
//customize
public string customPose = null;
public Vector2? lookAnLocation = null;
public Rot4? rotOverride = null;
//variation
public string variation = null;
public Color? variationColor;
public colorOverrideMode variationColorMode = colorOverrideMode.Default;
//TODO. age setting?
public int minDrawAge = -1;
public int maxDrawAge = -1;
2023-04-04 01:47:49 +00:00
public void SetBone(Bone bone)
{
this.bone = bone;
}
2022-08-14 20:46:19 +00:00
public void SetCustomPose(string newPose, bool autoUpdate = true, bool autoSetPawnGraphicDirty = false)
{
if (customPose == newPose)
return;
2023-04-04 01:47:48 +00:00
if(SizedApparelSettings.Debug)
Log.Message("[SizedApparel] Setting Custom Pose : " + newPose);
2022-08-14 20:46:19 +00:00
customPose = newPose;
if (autoUpdate)
2023-04-04 01:47:48 +00:00
{
2022-08-14 20:46:19 +00:00
this.UpdateGraphic();
2023-04-04 01:47:48 +00:00
this.lastPoseTick = Find.TickManager.TicksGame;
}
2022-08-14 20:46:19 +00:00
if(autoSetPawnGraphicDirty)
{
if (pawn == null)
return;
PortraitsCache.SetDirty(pawn);
GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(pawn);
}
}
2023-04-04 01:48:55 +00:00
public bool CheckCanPose(string targetPose, bool checkApparels, bool checkBodyParts, bool mustMatchSize, bool mustMatchBodytype, bool mustMatchRace)
2022-08-14 20:46:19 +00:00
{
if (checkApparels)
{
2023-04-04 01:47:49 +00:00
if (!SizedApparelUtility.CanPoseApparels(pawn, targetPose, currentHediffName, currentSeverityInt, cappedSeverityInt))
return false;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:48 +00:00
if (checkBodyParts)
{
2023-04-04 01:48:55 +00:00
Graphic graphic = GetBodyPartGraphics(false, mustMatchSize, mustMatchBodytype, mustMatchRace);
Graphic graphicH = GetBodyPartGraphics(true, mustMatchSize, mustMatchBodytype, mustMatchRace);
2023-04-04 01:47:48 +00:00
if (graphic != null || graphicH != null)
return true;
return false;
}
return true;
2022-08-14 20:46:19 +00:00
}
//TODO...
public int currentSeverityInt = -1;
public int cappedSeverityInt = 1000; // supported severity from worn apparel graphics
2023-04-04 01:47:49 +00:00
public Vector2 pivot = Vector2.zero;
2022-08-14 20:46:19 +00:00
public Vector2 position = Vector2.zero;//offset from pivot //UV. not pixel
2023-04-04 01:47:49 +00:00
public SizedApparelTexturePointDef points;
public SizedApparelTexturePointDef pointsHorny;
2022-08-14 20:46:19 +00:00
public float rotation = 0; // +: rotate right, -: rotate left
2023-04-04 01:47:49 +00:00
public float scale = 1f;
2022-08-14 20:46:19 +00:00
public Graphic bodyPartGraphic;
public Graphic bodyPartGraphicHorny;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
public Vector2 positionOffset = Vector2.zero; //offset from position //UV. not pixel
public Vector2 positionOffsetSouth = Vector2.zero;
public Vector2 positionOffsetNorth = Vector2.zero;
public Vector2 positionOffsetEast = Vector2.zero;
public Vector2 positionOffsetWest = Vector2.zero;
public float depthOffset = 0f;
2023-04-04 01:47:48 +00:00
//0.008f
2022-08-14 20:46:19 +00:00
public float depthOffsetEast = 0.008f;
public float depthOffsetWest = 0.008f;
public float depthOffsetSouth = 0.008f;
public float depthOffsetNorth = 0.008f;
2023-04-04 01:47:50 +00:00
2022-08-14 20:46:19 +00:00
//bigger = in front
public void SetDepthOffsets(float south, float north, float east, float west)
{
depthOffsetSouth = south;
depthOffsetNorth = north;
depthOffsetEast = east;
depthOffsetWest = west;
}
2023-04-04 01:47:49 +00:00
public void SetDepthOffsets(Depth4Offsets oppsets)
{
depthOffsetSouth = oppsets.south;
depthOffsetNorth = oppsets.north;
depthOffsetEast = oppsets.east;
depthOffsetWest = oppsets.west;
}
2022-08-14 20:46:19 +00:00
public void SetPositionOffsets(Vector2 south, Vector2 north, Vector2 east, Vector2 west)
{
positionOffsetSouth = south;
positionOffsetNorth = north;
positionOffsetEast = east;
positionOffsetWest = west;
}
2023-04-04 01:48:56 +00:00
public Graphic GetBodyPartGraphics(bool isHorny, bool mustMatchSize = false, bool mustMatchBodytype = false, bool mustMatchRace = false, string poseOverride = null, string variationOverride = null)
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:47:49 +00:00
SizedApparelTexturePointDef var;
2023-04-04 01:48:56 +00:00
return GetBodyPartGraphics(isHorny, out var, mustMatchBodytype, mustMatchSize, mustMatchRace, poseOverride, variationOverride);
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:48:55 +00:00
public Graphic GetBodyPartGraphics(bool isHorny, out SizedApparelTexturePointDef outPoints, bool mustMatchSize = false, bool mustMatchBodyType = false , bool mustMatchRace = false ,string poseOverride = null, string variationOverride = null)
2022-08-14 20:46:19 +00:00
{
if (pawn == null)
{
outPoints = null;
return null;
}
2023-04-04 01:47:48 +00:00
var comp = apparelRecorderCompCache;
if (comp == null)
comp = pawn.GetComp<ApparelRecorderComp>();
2022-08-14 20:46:19 +00:00
if (comp == null)
{
outPoints = null;
return null;
}
2023-04-04 01:47:49 +00:00
string bodyTypeString = pawn.story?.bodyType?.defName;
var key = new SizedApparelsDatabase.BodyPartDatabaseKey(pawn.def.defName, bodyTypeString, currentHediffName, customPath==null?bodyPartName: customPath, pawn.gender, Math.Min(currentSeverityInt, cappedSeverityInt), isHorny, poseOverride==null?customPose:poseOverride, variationOverride==null?variation: variationOverride);
2022-08-14 20:46:19 +00:00
var result = SizedApparelsDatabase.GetSupportedBodyPartPath(key, isBreast, customPath == null ? bodyPartName : customPath, defaultHediffName);
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
if (mustMatchSize)
if (Math.Min(currentSeverityInt, cappedSeverityInt) != result.size)
{
outPoints = null;
return null;
}
2023-04-04 01:47:50 +00:00
if (mustMatchBodyType)
{
if(result.bodyType != pawn.story?.bodyType?.defName)
{
outPoints = null;
return null;
}
}
2023-04-04 01:48:55 +00:00
if (mustMatchRace)
{
if (result.raceName != pawn.def.defName)
{
outPoints = null;
return null;
}
}
2022-08-14 20:46:19 +00:00
if (result.pathWithSizeIndex == null)
{
outPoints = null;
return null;
}
outPoints = result.points;
return GraphicDatabase.Get<Graphic_Multi>(result.pathWithSizeIndex);
}
public void UpdateGraphic()
{
bodyPartGraphic = GetBodyPartGraphics(false, out points, false);
bodyPartGraphicHorny = GetBodyPartGraphics(true, out pointsHorny, false);
}
public void UpdateGraphic(int index, int indexCapped = 1000)
{
this.currentSeverityInt = index;
this.cappedSeverityInt = indexCapped;
UpdateGraphic();
}
public void ResetTransform()
{
this.position = Vector2.zero;
2023-04-04 01:47:49 +00:00
this.scale = 1f;
2022-08-14 20:46:19 +00:00
this.rotation = 0;
}
public void ClearGraphics()
{
this.bodyPartGraphic = null;
this.bodyPartGraphicHorny = null;
this.points = null;
this.pointsHorny = null;
}
public void Clear()
{
currentHediffName = null;
currentSeverityInt = -1;
cappedSeverityInt = 1000;
customPose = null;
rotOverride = null;
ClearGraphics();
}
/*
public void SetHediffData(string name, int severityIndex , string variation = null)
{
currentHediffName = name;
currentSeverityInt = severityIndex;
}*/
public void SetHediffData(string name, int severityIndex, int cappedSeverityIndex = 1000, string variation = null)
{
currentHediffName = name;
currentSeverityInt = severityIndex;
this.cappedSeverityInt = cappedSeverityIndex;
this.variation = variation;
}
public void DrawBodyPart (Vector3 rootLoc, float angle, Rot4 facing, RotDrawMode bodyDrawType, PawnRenderFlags flags, Mesh bodyMesh)
{
if (!isVisible)
return;
2023-04-04 01:47:49 +00:00
if (scale == 0f)
2022-08-14 20:46:19 +00:00
return; //Don't draw if scale is zero
if (pawn == null)
return;
2023-04-04 01:47:50 +00:00
if (mustHaveBone && bone == null)
return;
2023-04-04 01:47:48 +00:00
if (bodyMesh == null)
{
if (SizedApparelSettings.Debug)
Log.Warning("[SizedApparel] DrawBodyParts But Null Body Mesh! : " + pawn.Name);
return;
}
2022-08-14 20:46:19 +00:00
PawnRenderer pawnRenderer = pawn.Drawer.renderer;
2023-04-04 01:48:55 +00:00
Shader shader = shader = ShaderDatabase.CutoutSkinOverlay;
2022-08-14 20:46:19 +00:00
Color drawColor1 = Color.white;
Color drawColor2 = Color.white;
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
bool forceWriteZ = true;
2023-04-04 01:47:48 +00:00
bool HasFurSkin = false;
//Furskin Check
if (pawn.Drawer.renderer.graphics.furCoveredGraphic != null)
{
HasFurSkin = true;
}
2023-04-04 01:47:49 +00:00
if (colorType == ColorType.Skin)
2022-08-14 20:46:19 +00:00
{
forceWriteZ = true;
if (bodyDrawType == RotDrawMode.Fresh)
{
2023-04-04 01:47:48 +00:00
if (HasFurSkin)
{
shader = pawn.Drawer.renderer.graphics.furCoveredGraphic.Shader;
if (!ShaderUtility.SupportsMaskTex(shader))
2023-04-04 01:48:55 +00:00
shader = shader = ShaderDatabase.CutoutSkinOverlay;
2023-04-04 01:47:48 +00:00
shader = pawnRenderer.graphics.furCoveredGraphic.Shader;
drawColor1 = pawnRenderer.graphics.furCoveredGraphic.Color;
drawColor2 = pawnRenderer.graphics.furCoveredGraphic.ColorTwo;
}
else
{
shader = pawn.Drawer.renderer.graphics.nakedGraphic.Shader;
2023-04-04 01:48:55 +00:00
if (!ShaderUtility.SupportsMaskTex(shader))
shader = shader = ShaderDatabase.CutoutSkinOverlay;
drawColor1 = pawnRenderer.graphics.nakedGraphic.Color;
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
}
}
else if (bodyDrawType == RotDrawMode.Rotting)
{
shader = pawn.Drawer.renderer.graphics.rottingGraphic.Shader;
if (!ShaderUtility.SupportsMaskTex(shader))
shader = shader = ShaderDatabase.CutoutSkinOverlay;
drawColor1 = pawnRenderer.graphics.rottingGraphic.Color;
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
}
}
else if (colorType == ColorType.Nipple)
{
forceWriteZ = true;
//Get SkinColor first.
if (bodyDrawType == RotDrawMode.Fresh)
{
if (HasFurSkin)
{
shader = pawn.Drawer.renderer.graphics.furCoveredGraphic.Shader;
2023-04-04 01:47:48 +00:00
if (!ShaderUtility.SupportsMaskTex(shader))
shader = ShaderDatabase.CutoutSkinOverlay;
2023-04-04 01:48:55 +00:00
shader = pawnRenderer.graphics.furCoveredGraphic.Shader;
drawColor1 = pawnRenderer.graphics.furCoveredGraphic.Color;
drawColor2 = pawnRenderer.graphics.furCoveredGraphic.ColorTwo;
}
else
{
shader = pawn.Drawer.renderer.graphics.nakedGraphic.Shader;
if (!ShaderUtility.SupportsMaskTex(shader))
shader = shader = ShaderDatabase.CutoutSkinOverlay;
2023-04-04 01:47:48 +00:00
drawColor1 = pawnRenderer.graphics.nakedGraphic.Color;
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
}
2022-08-14 20:46:19 +00:00
}
else if (bodyDrawType == RotDrawMode.Rotting)
{
shader = pawn.Drawer.renderer.graphics.rottingGraphic.Shader;
if (!ShaderUtility.SupportsMaskTex(shader))
2023-04-04 01:48:55 +00:00
shader = shader = ShaderDatabase.CutoutSkinOverlay;
2023-04-04 01:47:48 +00:00
drawColor1 = pawnRenderer.graphics.rottingGraphic.Color;
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:48 +00:00
2023-04-04 01:48:55 +00:00
if(apparelRecorderCompCache != null)
{
if(apparelRecorderCompCache.nippleColor != null)
{
drawColor1 = apparelRecorderCompCache.nippleColor.Value; //* drawColor1;
drawColor2 = apparelRecorderCompCache.nippleColor.Value; //* drawColor2; //maybe can be issue
}
else
{
//nipple Color is null
//Ust Default Color for Nipple with SkinColor
drawColor1 = defaultNippleColor * drawColor1;
drawColor2 = defaultNippleColor * drawColor2;
}
}
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
else if (colorType == ColorType.Hair)
2022-08-14 20:46:19 +00:00
{
forceWriteZ = false;
shader = ShaderDatabase.Transparent;
if(pawn.story != null)
2023-04-04 01:47:48 +00:00
drawColor1 = pawn.story.HairColor;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
else if (colorType == ColorType.Custom)
2022-08-14 20:46:19 +00:00
{
forceWriteZ = true;
shader = ShaderDatabase.Transparent;
if(customColorOne != null)
drawColor1 = customColorOne.Value;
if (customColorTwo != null)
drawColor2 = customColorTwo.Value;
}
2023-04-04 01:47:49 +00:00
else if (colorType == ColorType.None)
2022-08-14 20:46:19 +00:00
{
forceWriteZ = false;
shader = ShaderDatabase.Cutout;
}
2023-04-04 01:48:55 +00:00
if (isOverlay)
{
if (shader == ShaderDatabase.Cutout)
shader = ShaderDatabase.Transparent;
else if (shader == ShaderDatabase.CutoutSkin || shader == ShaderDatabase.CutoutSkinColorOverride)
shader = ShaderDatabase.CutoutSkinOverlay;
else
shader = ShaderDatabase.Transparent;
}
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:49 +00:00
Mesh scaledBodyMesh;
BoneTransform boneTransform = null;
if(bone != null)
{
if (facing == Rot4.South)
{
boneTransform = bone.south;
}
else if (facing == Rot4.North)
{
boneTransform = bone.north;
}
else if (facing == Rot4.East)
{
boneTransform = bone.east;
}
else if (facing == Rot4.West)
{
boneTransform = bone.west;
if (boneTransform == null)
boneTransform = bone.east;
}
}
float drawScale = scale;
float drawRotation = angle;
Vector3 drawPosition = rootLoc;
2023-04-04 01:47:50 +00:00
if (this.pawn.ageTracker.CurLifeStage.bodyDrawOffset != null)
{
drawPosition += this.pawn.ageTracker.CurLifeStage.bodyDrawOffset.Value;
}
2023-04-04 01:47:49 +00:00
if (boneTransform != null)
{
//TODO fixed angle for IK?
2023-04-04 01:48:55 +00:00
float width = (bodyMesh.vertices[2].x - bodyMesh.vertices[0].x);
2023-04-04 01:47:49 +00:00
if (centeredTexture)
{
bool westUsingEast = false;
if (facing == Rot4.West && bone.west == null)
westUsingEast = true;
2023-04-04 01:48:55 +00:00
2023-04-04 01:47:49 +00:00
drawRotation = boneTransform.InitialAngle + boneTransform.angleOffset;
//not sure it work correct
Vector3 v = (boneTransform.InitialPosition + boneTransform.positionOffset) * width;
var q = Quaternion.AngleAxis (westUsingEast? -angle : angle, Vector3.up);
v = q * v;
drawPosition = (v) ; // calculate rotated point
//Log.Message(boneTransform.angleOffset.ToString());
if (westUsingEast)
{
//already scaled with "width"?
//its using east as west. so flip position
drawPosition.Scale(new Vector3(-1f,0f,1f));
//drawRotation = boneTransform.InitialAngle - boneTransform.angleOffset;
}
drawPosition += rootLoc;
drawRotation += angle ;
}
else
2023-04-04 01:48:55 +00:00
{
//NotCentered Texture not yet support Rotation System.
2023-04-04 01:47:49 +00:00
//wip
2023-04-04 01:48:55 +00:00
Vector3 v = Vector3.zero;
2023-04-04 01:47:49 +00:00
bool westUsingEast = false;
if (facing == Rot4.West && bone.west == null)
westUsingEast = true;
2023-04-04 01:48:55 +00:00
drawRotation = boneTransform.InitialAngle; // + boneTransform.angleOffset
2023-04-04 01:47:49 +00:00
//not sure it work correct
2023-04-04 01:48:55 +00:00
//Vector3 v = (boneTransform.InitialPosition); //initialpos as custom pivot
//var q = Quaternion.AngleAxis(westUsingEast ? -drawRotation : drawRotation, Vector3.up);
//var q2 = Quaternion.AngleAxis(westUsingEast ? -angle : angle, Vector3.up);
//v = q * v; // calculate final draw position with torso angle
//v = v + boneTransform.InitialPosition + boneTransform.positionOffset;
//v = q2 * v; //NotCentered Texture not yet support Rotation System.
2023-04-04 01:47:49 +00:00
drawPosition = (v) * width;
if (westUsingEast)
{
//already scaled with "width"?
//its using east as west. so flip position
drawPosition.Scale(new Vector3(-1f, 0f, 1f));
//drawRotation = boneTransform.InitialAngle - boneTransform.angleOffset;
}
drawPosition += rootLoc ; // adjust with result
//Log.Message(boneTransform.angleOffset.ToString());
drawRotation += angle;
}
}
if (drawScale != 1f)
{
// scale Only Rimworld Plane Mesh
if(bodyMesh.vertexCount == 4)
{
float width = (bodyMesh.vertices[2].x - bodyMesh.vertices[0].x);
var meshSet = MeshPool.GetMeshSetForWidth(scale * width);
scaledBodyMesh = meshSet.MeshAt(facing);
}
else
scaledBodyMesh = bodyMesh;
}
else
{
scaledBodyMesh = bodyMesh;
}
Quaternion quaternion = Quaternion.AngleAxis(drawRotation, Vector3.up);
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:50 +00:00
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
Rot4 targetRot = facing;
if (rotOverride != null)
targetRot = rotOverride.Value;
if (targetRot == Rot4.South)
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset(positionOffsetSouth, scaledBodyMesh);
drawPosition.x += loc.x;
drawPosition.z += loc.y;
drawPosition.y += depthOffsetSouth;
2022-08-14 20:46:19 +00:00
}
else if(targetRot == Rot4.North)
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset(positionOffsetNorth, scaledBodyMesh);
drawPosition.x += loc.x;
drawPosition.z += loc.y;
drawPosition.y += depthOffsetNorth;
2022-08-14 20:46:19 +00:00
}
else if (targetRot == Rot4.East)
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset(positionOffsetEast, scaledBodyMesh);
drawPosition.x += loc.x;
drawPosition.z += loc.y;
drawPosition.y += depthOffsetEast;
2022-08-14 20:46:19 +00:00
}
else if (targetRot == Rot4.West)
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset(positionOffsetWest, scaledBodyMesh);
drawPosition.x += loc.x;
drawPosition.z += loc.y;
drawPosition.y += depthOffsetWest;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
Graphic graphic = null;
if (SizedApparelUtility.IsHorny(pawn))
graphic = bodyPartGraphicHorny;
if (graphic == null)
graphic = bodyPartGraphic;
if (graphic == null)
return;
2023-04-04 01:47:48 +00:00
//ForFurskinOffset
if(bodyDrawType == RotDrawMode.Fresh && HasFurSkin && AutoOffsetForFurCoveredBody)
{
//vector.y += 0.009187258f; //in PawnRenderer, it adds 0.009187258f.
//graphic.maskPath does error? need to check
// worn fur body and naked fur body has different offsets... wtf
//TODO Need to Fix
}
2022-08-14 20:46:19 +00:00
Material mat;
2023-04-04 01:47:48 +00:00
2023-04-04 01:48:55 +00:00
//If ForceWriteDepth, draw Cutout mesh for write depth. this is for avoid problem of body addons drawing under other meshes. such as breasts front of body but behind tail.
if (forceWriteZ || (!flags.FlagSet(PawnRenderFlags.Cache) && !isOverlay))// //(!flags.FlagSet(PawnRenderFlags.Cache) && !isOverlay)
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:48:55 +00:00
Graphic Zgraphic = graphic.GetColoredVersion(ShaderDatabase.CutoutComplex, drawColor1, drawColor2); // ShaderDatabase.Cutout
Vector3 drawPositionZ = drawPosition;
drawPositionZ.y = 0; //Send Back of body, but it can still write depth.
mat = flags.FlagSet(PawnRenderFlags.Cache) ? Zgraphic.MatAt(targetRot) : (Material)overrideMatMethod.Invoke(pawnRenderer, new object[] { Zgraphic.MatAt(facing), pawn, flags.FlagSet(PawnRenderFlags.Portrait) });
GenDraw.DrawMeshNowOrLater(scaledBodyMesh, drawPositionZ, quaternion, mat, flags.FlagSet(PawnRenderFlags.DrawNow)); // draw for writeZ data to solve shadow issue
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:48:55 +00:00
//shader must be mask
2022-08-14 20:46:19 +00:00
graphic = graphic.GetColoredVersion(shader, drawColor1, drawColor2);
2023-04-04 01:48:55 +00:00
if (graphic.maskPath == null)
{
//Test
//graphic.maskPath = texturePath_Red;
}
//drawPosition.y += 0.00001f;
2022-08-14 20:46:19 +00:00
mat = flags.FlagSet(PawnRenderFlags.Cache) ? graphic.MatAt(targetRot) : (Material)overrideMatMethod.Invoke(pawnRenderer, new object[] { graphic.MatAt(facing), pawn, flags.FlagSet(PawnRenderFlags.Portrait) });
2023-04-04 01:47:49 +00:00
GenDraw.DrawMeshNowOrLater(scaledBodyMesh, drawPosition, quaternion, mat, flags.FlagSet(PawnRenderFlags.DrawNow));
2022-08-14 20:46:19 +00:00
}
}
//TODO: Torso Pose?
2023-04-04 01:47:49 +00:00
public class BodyDef : Def
2022-08-14 20:46:19 +00:00
{
//public List<SizedApparelBodyPartDef> BodyParts;
//defName = raceName ?? could it work?
2023-04-04 01:47:49 +00:00
public List<BodyWithBodyType> bodies = new List<BodyWithBodyType>();
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:49 +00:00
//public List<BodyTypeAndOffset> penisOffset;
//public List<BodyTypeAndOffset> vaginaOffset;
//public List<BodyTypeAndOffset> pubicHairOffset;
//public List<BodyTypeAndOffset> udderOffset;
//public List<BodyTypeAndOffset> bellyOffset;
//public List<BodyTypeAndOffset> breastsOffset;
//public List<BodyTypeAndOffset> anusOffset;
2022-08-14 20:46:19 +00:00
}
public class SizedApparelBody
{
public string customPoseOfBody = null;
public bool canCustomPose()
{
//check apparels
return false;
}
}
2023-04-04 01:47:49 +00:00
public class SizedApparelBodyPartOfssetDef : Def
{
//defName IsRaceName
}
2022-08-14 20:46:19 +00:00
}