Weapon grip anchor: gizmo-authored WYSIWYG grip + operator's hand-tuned grip baked

WeaponGripAnchor under Hand_R in Player.prefab carries the actual axe
mesh as an EditorOnly preview (baker skips it; play-verified single axe)
so grip manipulation is the real model live in the prefab stage;
AttachMeleeWeapon reads the anchor (consts = seed/fallback). Includes
the operator's first gizmo-tuned grip (rebaked mesh matches expected
radius exactly). PlayerRigTools also carries the heavy-lock clip
re-speeds (1.0/0.85/0.88) consumed by the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 00:13:48 -07:00
parent 2710d680b4
commit 7f032571df
5 changed files with 428 additions and 106 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,137 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Weapon_Preview
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: 48dd9a7736c11b5448a74711310e2ee6, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: 48dd9a7736c11b5448a74711310e2ee6, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3355021883474362010
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b1208b4fab8c6c84c9655fcc3da69efe
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
+185 -94
View File
@@ -543,6 +543,96 @@ Transform:
- {fileID: 8387096127486878932}
m_Father: {fileID: 3285138630030188913}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1276864559111187745
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7059114398337531242}
- component: {fileID: 3316843276031498534}
- component: {fileID: 195853059780659648}
m_Layer: 0
m_Name: WeaponGripAnchor
m_TagString: EditorOnly
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7059114398337531242
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1276864559111187745}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0.06, z: 0.02}
m_LocalScale: {x: 1.25, y: 1.25, z: 1.25}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7714500739160558408}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &3316843276031498534
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1276864559111187745}
m_Mesh: {fileID: 4300000, guid: 779990e366a12f94ebf06711efef57d3, type: 3}
--- !u!23 &195853059780659648
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1276864559111187745}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: b1208b4fab8c6c84c9655fcc3da69efe, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &1325769919043544559
GameObject:
m_ObjectHideFlags: 0
@@ -833,7 +923,7 @@ Transform:
- {fileID: 100450397525093210}
- {fileID: 795319999662639885}
- {fileID: 1063840860590240591}
- {fileID: 136525282400229282}
- {fileID: 1295007706386937831}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8290370249712404227
@@ -2239,6 +2329,99 @@ Transform:
- {fileID: 6528362243562002222}
m_Father: {fileID: 2241067459902915878}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &4881940712018230872
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1295007706386937831}
- component: {fileID: 1300809461631886519}
m_Layer: 0
m_Name: SM_Wep_Melee
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1295007706386937831
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4881940712018230872}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 194292233036670670}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!137 &1300809461631886519
SkinnedMeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4881940712018230872}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 3
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 53abed4a039a2944498dc0d43258baf5, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
m_SkinnedMotionVectors: 1
m_Mesh: {fileID: 4300000, guid: 0d76db2ce815146419fd52c745104909, type: 2}
m_Bones:
- {fileID: 7714500739160558408}
m_BlendShapeWeights: []
m_RootBone: {fileID: 7714500739160558408}
m_AABB:
m_Center: {x: 0.41444796, y: 0.059999853, z: 0.02000001}
m_Extent: {x: 0.6914556, y: 0.07196632, z: 0.31731963}
m_DirtyAABB: 0
--- !u!1 &5369485156592443817
GameObject:
m_ObjectHideFlags: 0
@@ -2949,99 +3132,6 @@ MeshRenderer:
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &7369922033552706299
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 136525282400229282}
- component: {fileID: 4540681531604194498}
m_Layer: 0
m_Name: SM_Wep_Melee
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &136525282400229282
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7369922033552706299}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 194292233036670670}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!137 &4540681531604194498
SkinnedMeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7369922033552706299}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 3
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 53abed4a039a2944498dc0d43258baf5, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
m_SkinnedMotionVectors: 1
m_Mesh: {fileID: 4300000, guid: 0d76db2ce815146419fd52c745104909, type: 2}
m_Bones:
- {fileID: 7714500739160558408}
m_BlendShapeWeights: []
m_RootBone: {fileID: 7714500739160558408}
m_AABB:
m_Center: {x: 0.41444784, y: 0.060000006, z: 0.02000001}
m_Extent: {x: 0.6914554, y: 0.071966305, z: 0.31731963}
m_DirtyAABB: 0
--- !u!1 &7432474647424217847
GameObject:
m_ObjectHideFlags: 0
@@ -3488,6 +3578,7 @@ Transform:
- {fileID: 133776596909499235}
- {fileID: 8397794599406007036}
- {fileID: 7135072282735691904}
- {fileID: 7059114398337531242}
m_Father: {fileID: 5022670406498921928}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &8130402508941367653
@@ -674,9 +674,9 @@ namespace ProjectM.EditorTools
// window. Hit-start seconds measured off the pack takes (= the WindUp take length).
var swaps = new (string state, string fbxSub, string clip, float speed)[]
{
("Swing1", "Attack/LightCombo01/A_Attack_LightCombo01A_Sword", "A_Attack_LightCombo01A_Sword", 1.25f), // hit @0.33s -> ~0.27s (07-19 retime: 26-tick window)
("Swing2", "Attack/LightCombo01/A_Attack_LightCombo01B_Sword", "A_Attack_LightCombo01B_Sword", 1.0f), // hit @0.17s natural speed
("Swing3", "Attack/LightCombo01/A_Attack_LightCombo01C_Sword", "A_Attack_LightCombo01C_Sword", 1.1f), // finisher: hit @0.33s, heaviest of the chain
("Swing1", "Attack/LightCombo01/A_Attack_LightCombo01A_Sword", "A_Attack_LightCombo01A_Sword", 1.0f), // 07-21 heavy lock: TRUE contact 0.333s = 20t (matches MeleeContactTicks 20)
("Swing2", "Attack/LightCombo01/A_Attack_LightCombo01B_Sword", "A_Attack_LightCombo01B_Sword", 0.85f), // TRUE contact 0.196s = 12t (knob step2 = round(20*0.625) = 12)
("Swing3", "Attack/LightCombo01/A_Attack_LightCombo01C_Sword", "A_Attack_LightCombo01C_Sword", 0.88f), // finisher: TRUE contact 0.417s = 25t (knob step3 = 25; 1 tick under CastFacingTicks)
("SpecialSlam", "Attack/HeavyCombo01/A_Attack_HeavyCombo01A_Sword", "A_Attack_HeavyCombo01A_Slam_Sword", 1.5f), // custom take (skips ~2/3 of the windup); hit ~0.35s
("Death", "Death/A_Death_B_01_Sword", "A_Death_B_01_Sword", 1.0f), // backward collapse, holds last frame
};
@@ -784,7 +784,22 @@ namespace ProjectM.EditorTools
// Rigid rest matrices (GraftSmr conventions: scale-stripped rest, feet-at-0 prefab space).
var hm = hand.localToWorldMatrix;
var handRigid = Matrix4x4.TRS((Vector3)hm.GetColumn(3), hm.rotation, Vector3.one);
var grip = Matrix4x4.TRS(WeaponGripPos, Quaternion.Euler(WeaponGripEuler), Vector3.one * WeaponScale); // uniform scale: normals keep their directions
// 07-20 designer flow: the WeaponGripAnchor child of Hand_R (EditorOnly, carries a live preview of
// the weapon mesh) is the AUTHORING source when present -- move/rotate/scale it with normal gizmos
// in the prefab stage, then re-run this tool to bake. The consts are the fallback/seed only.
Vector3 gripPos = WeaponGripPos;
Quaternion gripRot = Quaternion.Euler(WeaponGripEuler);
float gripScale = WeaponScale;
var gripAnchor = hand.Find("WeaponGripAnchor");
if (gripAnchor != null)
{
gripPos = gripAnchor.localPosition;
gripRot = gripAnchor.localRotation;
gripScale = gripAnchor.localScale.x; // uniform: gizmo-scaled non-uniformly -> x wins
if (Mathf.Abs(gripAnchor.localScale.y - gripScale) > 1e-3f || Mathf.Abs(gripAnchor.localScale.z - gripScale) > 1e-3f)
Debug.LogWarning("[PlayerRigTools] WeaponGripAnchor scale is non-uniform; using X for the rigid bake.");
}
var grip = Matrix4x4.TRS(gripPos, gripRot, Vector3.one * gripScale); // uniform scale: normals keep their directions
var toWorld = handRigid * grip;
var verts = srcMesh.vertices;
@@ -838,8 +853,79 @@ namespace ProjectM.EditorTools
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
Debug.Log($"[PlayerRigTools] Melee weapon attached to Hand_R ({srcMesh.vertexCount}v, grip pos {WeaponGripPos} euler {WeaponGripEuler}).");
Debug.Log($"[PlayerRigTools] Melee weapon attached to Hand_R ({srcMesh.vertexCount}v). Grip source: WeaponGripAnchor if present (gizmo-authored), else the consts. Re-run after moving the anchor.");
}
/// <summary>07-20 designer grip flow: create (or refresh the preview on) the WeaponGripAnchor under
/// Hand_R in Player.prefab. The anchor CARRIES the actual weapon mesh as an edit-time preview (plain
/// MeshRenderer + a URP/Lit material with the pack atlas), so what you grab with the move/rotate/scale
/// gizmos in the prefab stage IS the axe at true size, live. Tagged <b>EditorOnly</b> -- the Entities
/// baker skips it, so it can never leak an un-animated duplicate into the game; the baked rigid-skinned
/// SMR stays the only runtime weapon. Until you re-run Attach Melee Weapon you'll see BOTH (preview at
/// the new grip, baked axe at the old) -- a free before/after ghost; when they coincide, you're baked.
/// Seeds from the consts ONLY on first creation; re-runs never stomp a hand-tuned anchor.</summary>
[MenuItem("ProjectM/Animation/Player - Create Weapon Grip Anchor (LANTERN)")]
public static void CreateWeaponGripAnchor()
{
const string weaponFbx = "Assets/Synty/PolygonDungeonRealms/Models/SM_Wep_Axe_Large_01.fbx";
const string atlasPath = "Assets/Synty/PolygonDungeonRealms/Textures/Dungeons_2_Texture_01_A.png";
const string previewMatPath = "Assets/_Project/Materials/M_Weapon_Preview.mat";
const string output = "Assets/_Project/Prefabs/Player.prefab";
var src = AssetDatabase.LoadAssetAtPath<GameObject>(weaponFbx);
var srcMf = src != null ? src.GetComponentInChildren<MeshFilter>(true) : null;
if (srcMf == null || srcMf.sharedMesh == null) { Debug.LogError($"[PlayerRigTools] Weapon fbx/mesh missing: {weaponFbx}"); return; }
// Plain URP/Lit preview material (the runtime deformation shader won't render on a static MeshRenderer).
var mat = AssetDatabase.LoadAssetAtPath<Material>(previewMatPath);
if (mat == null)
{
var lit = Shader.Find("Universal Render Pipeline/Lit");
if (lit == null) { Debug.LogError("[PlayerRigTools] URP/Lit shader not found."); return; }
mat = new Material(lit) { name = "M_Weapon_Preview" };
var atlas = AssetDatabase.LoadAssetAtPath<Texture>(atlasPath);
if (atlas != null && mat.HasProperty("_BaseMap")) mat.SetTexture("_BaseMap", atlas);
AssetDatabase.CreateAsset(mat, previewMatPath);
}
var root = PrefabUtility.LoadPrefabContents(output);
try
{
Transform hand = null;
foreach (var t in root.GetComponentsInChildren<Transform>(true)) if (t.name == "Hand_R") { hand = t; break; }
if (hand == null) { Debug.LogError("[PlayerRigTools] Hand_R not found on Player.prefab."); return; }
var anchor = hand.Find("WeaponGripAnchor");
bool fresh = anchor == null;
if (fresh)
{
var go = new GameObject("WeaponGripAnchor");
anchor = go.transform;
anchor.SetParent(hand, false);
// Seed exactly where the current bake sits, so the preview and the baked axe start coincident.
anchor.localPosition = WeaponGripPos;
anchor.localRotation = Quaternion.Euler(WeaponGripEuler);
anchor.localScale = Vector3.one * WeaponScale;
}
anchor.gameObject.tag = "EditorOnly"; // the Entities baker SKIPS EditorOnly -- preview never reaches runtime
var mf = anchor.GetComponent<MeshFilter>();
if (mf == null) mf = anchor.gameObject.AddComponent<MeshFilter>();
mf.sharedMesh = srcMf.sharedMesh;
var mr = anchor.GetComponent<MeshRenderer>();
if (mr == null) mr = anchor.gameObject.AddComponent<MeshRenderer>();
mr.sharedMaterial = mat;
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
PrefabUtility.SaveAsPrefabAsset(root, output);
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
Debug.Log("[PlayerRigTools] WeaponGripAnchor ready under Hand_R (EditorOnly, live axe preview). "
+ "Open Player.prefab, search 'WeaponGripAnchor', move/rotate/scale it with normal gizmos, then run "
+ "'Player - Attach Melee Weapon' to bake. Preview + baked axe overlap = you're in sync.");
}
}
}