NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

meshValidation.py (8533B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\evegraphics\validation\validators\meshValidation.py
      2 import os
      3 import blue
      4 import trinity
      5 from trinity import effects
      6 from evegraphics.validation.commonUtilities import Validate
      7 from evegraphics.validation.validationFunctions import ValidateResPath
      8 from evegraphics.validation import resources
      9 from evegraphics.validation.validators.effectValidation import effectCache
     10 MAX_BONES_PER_MESH = 58
     11 
     12 def _GetGranny(context, resPath):
     13     return resources.GetResource(context, resPath, resources.ResourceType.GRANNY)
     14 
     15 
     16 @Validate('Tr2Mesh')
     17 def ValidResPath(context, mesh):
     18     ValidateResPath(context, mesh, 'geometryResPath', extensions=('.gr2',))
     19 
     20 
     21 @Validate('Tr2InstancedMesh')
     22 def ValidInstanceResPath(context, mesh):
     23     if mesh.instanceGeometryResPath:
     24         ValidateResPath(context, mesh, 'instanceGeometryResPath', extensions=('.gr2',))
     25     elif not mesh.instanceGeometryResource:
     26         context.Error(mesh, 'instanced mesh has no instance geometry resource')
     27 
     28 
     29 @Validate('Tr2Mesh')
     30 def ValidMeshIndex(context, mesh):
     31     granny = _GetGranny(context, mesh.geometryResPath)
     32     if not granny:
     33         return
     34     if mesh.meshIndex < 0 or mesh.meshIndex >= len(granny.meshes):
     35         context.Error(mesh, 'mesh index is out of range (should be between 0 and %s)' % (len(granny.meshes) - 1))
     36 
     37 
     38 def _DeleteObject(context, default):
     39     obj = context.GetTop()
     40     root = context.GetStack()[0]
     41 
     42     def inner():
     43         for route in blue.FindRoute(root, obj):
     44             last = route[-1]
     45             if last[1] == 0:
     46                 setattr(last[0], last[2], None)
     47             else:
     48                 last[0].remove(obj)
     49 
     50         return True
     51 
     52     return ('Delete %s' % (getattr(obj, 'name', '') or type(obj).__name__), inner, default)
     53 
     54 
     55 @Validate('Tr2Mesh')
     56 def HasAreas(context, mesh):
     57     if len(_GetAllAreas(mesh)) == 0:
     58         actions = []
     59         if len(context.GetStack()) > 1:
     60             actions.append(_DeleteObject(context, True))
     61         context.Error(mesh, 'has no areas', actions=actions)
     62 
     63 
     64 @Validate('Tr2MeshArea')
     65 def AreaContainsEffect(context, area):
     66     if not area.effect:
     67         context.Error(area, 'mesh area without an effect')
     68 
     69 
     70 def _GetMeshAreaCount(granny, meshIndex):
     71     try:
     72         return len(granny.meshes[meshIndex].trigroups)
     73     except IndexError:
     74         return 0
     75 
     76 
     77 @Validate('Tr2Mesh(mesh)/.../Tr2MeshArea(area)')
     78 def ValidMeshAreaIndex(context, mesh, area):
     79     granny = _GetGranny(context, mesh.geometryResPath)
     80     if not granny:
     81         return
     82     count = _GetMeshAreaCount(granny, mesh.meshIndex)
     83     if area.index < 0 or area.index + area.count > count:
     84         context.Error(area, 'mesh area index or count is out of range (should be between 0 and %s)' % (count - 1))
     85 
     86 
     87 GRANNY_VERTEX_ELEMENT_TO_USAGE = {'Position': 0,
     88  'DiffuseColor': 1,
     89  'Normal': 2,
     90  'Tangent': 3,
     91  'Binormal': 4,
     92  'TextureCoordinates': 5,
     93  'BoneIndices': 6,
     94  'BoneWeights': 7}
     95 USAGE_TO_GRANNY_VERTEX_ELEMENT = {v:k for k, v in GRANNY_VERTEX_ELEMENT_TO_USAGE.items()}
     96 
     97 def _GrannyVertexElementToTrinityUsage(name, offset):
     98     for k, v in GRANNY_VERTEX_ELEMENT_TO_USAGE.items():
     99         if name.startswith(k):
    100             if name == k:
    101                 return (v, offset)
    102             else:
    103                 return (v, int(name[len(k):]) + offset)
    104 
    105 
    106 def _TrinityUsageToGrannyVertexElement(usage, usageIndex):
    107     name = USAGE_TO_GRANNY_VERTEX_ELEMENT.get(usage, 'Unknown(%s)' % usage)
    108     if usageIndex:
    109         name += str(usageIndex)
    110     return name
    111 
    112 
    113 def _GetAllAreas(mesh):
    114     areas = []
    115     if mesh.opaqueAreas:
    116         areas.extend(mesh.opaqueAreas)
    117     if mesh.decalAreas:
    118         areas.extend(mesh.decalAreas)
    119     if mesh.depthAreas:
    120         areas.extend(mesh.depthAreas)
    121     if mesh.transparentAreas:
    122         areas.extend(mesh.transparentAreas)
    123     if mesh.additiveAreas:
    124         areas.extend(mesh.additiveAreas)
    125     if mesh.pickableAreas:
    126         areas.extend(mesh.pickableAreas)
    127     if mesh.mirrorAreas:
    128         areas.extend(mesh.mirrorAreas)
    129     if mesh.decalNormalAreas:
    130         areas.extend(mesh.decalNormalAreas)
    131     if mesh.depthNormalAreas:
    132         areas.extend(mesh.depthNormalAreas)
    133     if mesh.opaquePrepassAreas:
    134         areas.extend(mesh.opaquePrepassAreas)
    135     if mesh.decalPrepassAreas:
    136         areas.extend(mesh.decalPrepassAreas)
    137     if mesh.geometryEraserAreas:
    138         areas.extend(mesh.geometryEraserAreas)
    139     if mesh.distortionAreas:
    140         areas.extend(mesh.distortionAreas)
    141     return areas
    142 
    143 
    144 def _GetAllShaderInputs(mesh):
    145     areas = _GetAllAreas(mesh)
    146     inputs = set()
    147     for each in areas:
    148         if each.effect:
    149             inputs.update(effects.GetVertexShaderInputs(each.effect, effectCache))
    150 
    151     return inputs
    152 
    153 
    154 def _SubtractVertexInputs(context, mesh, path, inputs, grannyName, offset):
    155     granny = _GetGranny(context, path)
    156     if not granny:
    157         return False
    158     try:
    159         grannyMesh = granny.meshes[mesh.meshIndex]
    160     except IndexError:
    161         return False
    162 
    163     decl = grannyMesh.GetVertexDeclaration()
    164     for each in decl.keys():
    165         i = _GrannyVertexElementToTrinityUsage(each, offset)
    166         if i is None:
    167             context.Error(mesh, '%s granny file contains invalid vertex element %s' % (grannyName, each))
    168         elif i in inputs:
    169             inputs.remove(i)
    170 
    171     return True
    172 
    173 
    174 def _ValidVertexShaderInputs(context, mesh, path, inputs, grannyName):
    175     if not _SubtractVertexInputs(context, mesh, path, inputs, grannyName, 0):
    176         return
    177     err = []
    178     for usage, usageIndex in inputs:
    179         if (usage, usageIndex) == (5, 1):
    180             continue
    181         codeUsages = []
    182         for obj in context.GetStack():
    183             if getattr(obj, 'GetVertexElementAddedThroughCode', None) and callable(obj.GetVertexElementAddedThroughCode):
    184                 codeUsages = obj.GetVertexElementAddedThroughCode()
    185 
    186         if (usage, usageIndex) in codeUsages:
    187             continue
    188         err.append(_TrinityUsageToGrannyVertexElement(usage, usageIndex))
    189 
    190     if err:
    191         context.Error(mesh, '%s: %s granny file is missing vertex elements required by the shader: %s' % (grannyName, path, ', '.join(err)))
    192 
    193 
    194 def GetGrannyBoneCount(context, path):
    195     granny = _GetGranny(context, path)
    196     if not granny:
    197         return 0
    198     return max(granny.GetMeshesJointCount())
    199 
    200 
    201 def GetBoneCountFromLoadedGranny(grannyObject):
    202     return max(grannyObject.GetMeshesJointCount())
    203 
    204 
    205 def GetMeshBoneCount(context, mesh):
    206     return GetGrannyBoneCount(context, mesh.geometryResPath)
    207 
    208 
    209 @Validate('=Tr2Mesh')
    210 def ValidMeshVertexShaderInputs(context, mesh):
    211     inputs = _GetAllShaderInputs(mesh)
    212     _ValidVertexShaderInputs(context, mesh, mesh.geometryResPath, inputs, '')
    213 
    214 
    215 @Validate('Tr2InstancedMesh')
    216 def ValidInstancedMeshVertexShaderInputs(context, mesh):
    217     inputs = _GetAllShaderInputs(mesh)
    218     if not _SubtractVertexInputs(context, mesh, mesh.geometryResPath, inputs, '', 0):
    219         return
    220     ignoreInstanced = not _SubtractVertexInputs(context, mesh, mesh.instanceGeometryResPath, inputs, '', 8)
    221     err = []
    222     for usage, usageIndex in inputs:
    223         if ignoreInstanced and usageIndex >= 8:
    224             continue
    225         err.append(_TrinityUsageToGrannyVertexElement(usage, usageIndex))
    226 
    227     if err:
    228         context.Error(mesh, 'granny file "%s" is missing vertex elements required by the shader: %s' % (mesh.geometryResPath, ', '.join(err)))
    229 
    230 
    231 @Validate('Tr2Mesh')
    232 def MaxBonesLimit(context, mesh):
    233     count = GetGrannyBoneCount(context, mesh.geometryResPath)
    234     if count > MAX_BONES_PER_MESH:
    235         context.Error(mesh, 'granny file contains %s bones; maximum allowed count is %s' % (count, MAX_BONES_PER_MESH))
    236 
    237 
    238 def _IsSkinned(context, resPath):
    239     granny = _GetGranny(context, resPath)
    240     return IsGrannySkinned(granny)
    241 
    242 
    243 def IsGrannySkinned(grannyObject):
    244     if not grannyObject:
    245         return None
    246     for each in grannyObject.meshes:
    247         if 'BoneIndices' in each.GetVertexDeclaration():
    248             return True
    249 
    250     return False
    251 
    252 
    253 def IsMeshSkinned(context, mesh):
    254     return _IsSkinned(context, mesh.geometryResPath)
    255 
    256 
    257 @Validate('Tr2Mesh(mesh)/.../Tr2Effect(effect)')
    258 def SkinnedUnskinnedMismatch(context, mesh, effect):
    259     if not effect.effectFilePath:
    260         return
    261     isSkinned = IsMeshSkinned(context, mesh)
    262     if isSkinned is None:
    263         return
    264     isEffectSkinned = 'skinned_' in effect.effectFilePath.lower()
    265     if not isSkinned and isEffectSkinned:
    266         context.Error(effect, 'skinned effect is applied to a non-skinned mesh')