particleSystemValidation.py (10261B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\evegraphics\validation\validators\particleSystemValidation.py 2 import blue 3 import trinity 4 from trinity import effects, particlesystemelements 5 from evegraphics.validation.commonUtilities import Validate, DeleteAction 6 from evegraphics.validation.validators.effectValidation import effectCache 7 8 @Validate('Tr2DynamicEmitter') 9 def RateIsValid(context, emitter): 10 if not context.IsBoundAsDestination(emitter, 'rate') and emitter.rate <= 0: 11 context.Error(emitter, 'rate needs to be positive') 12 13 14 @Validate('Tr2DynamicEmitter') 15 def MaxParticlesIsValid(context, emitter): 16 if not context.IsBoundAsDestination(emitter, 'maxParticles') and emitter.maxParticles == 0: 17 context.Error(emitter, 'maxParticles is zero - emitter will not emit') 18 19 20 @Validate('Tr2DynamicEmitter') 21 def HasParticleSystem(context, emitter): 22 if not emitter.particleSystem: 23 context.Error(emitter, 'emitter is not attached to a particle system') 24 25 26 @Validate('Tr2DynamicEmitter') 27 def DynamicEmitterIsValid(context, emitter): 28 if not emitter.isValid: 29 context.Error(emitter, 'emitter reports as being invalid') 30 31 32 @Validate('EveChildParticleSphere') 33 def ParticleSphereIsValid(context, sphere): 34 if not sphere.isValid: 35 context.Error(sphere, 'particle sphere reports as being invalid') 36 37 38 @Validate('Tr2DynamicEmitter') 39 def ConstantParticleSystemBoundToDynamicEmitter(context, emitter): 40 if emitter.particleSystem: 41 if not emitter.particleSystem.updateSimulation: 42 context.Error(emitter, 'dynamic emitter emits into a constant particle system (particle system updateSimulation flag is off)') 43 44 45 @Validate('Tr2ParticleSystem') 46 def ParticleSystemIsValid(context, particleSystem): 47 if not particleSystem.isValid: 48 context.Error(particleSystem, 'particle system reports as being invalid') 49 50 51 @Validate('Tr2ParticleSystem') 52 def ConstantParticleSystemBoundToDynamicEmitter(context, particleSystem): 53 if particleSystem.forces and not particleSystem.applyForce: 54 context.Error(particleSystem, 'particle system has forces yet applyForce flag is off') 55 56 57 @Validate('/*') 58 def ParticleSystemsUsed(context, root): 59 systems = set(blue.FindInterface(root, 'Tr2ParticleSystem')) 60 for each in list(systems): 61 if each.emitParticleOnDeathEmitter: 62 try: 63 systems.remove(each) 64 except KeyError: 65 pass 66 67 if each.emitParticleDuringLifeEmitter: 68 try: 69 systems.remove(each) 70 except KeyError: 71 pass 72 73 for each in blue.FindInterface(root, 'Tr2InstancedMesh'): 74 if each.instanceGeometryResource in systems: 75 systems.remove(each.instanceGeometryResource) 76 77 for each in systems: 78 context.Error(each, 'system is not used', actions=[DeleteAction(context.GetStack()[0], each, True)]) 79 80 81 @Validate('/*') 82 def ParticleSystemsHaveEmitters(context, root): 83 systems = set(blue.FindInterface(root, 'Tr2ParticleSystem')) 84 emitters = blue.FindInterface(root, 'Tr2DynamicEmitter') + blue.FindInterface(root, 'Tr2StaticEmitter') + blue.FindInterface(root, 'EveChildParticleSphere') 85 for each in list(systems): 86 if each.emitParticleOnDeathEmitter in systems: 87 systems.remove(each.emitParticleOnDeathEmitter) 88 if each.emitParticleDuringLifeEmitter in systems: 89 systems.remove(each.emitParticleDuringLifeEmitter) 90 91 for each in emitters: 92 if each.particleSystem in systems: 93 systems.remove(each.particleSystem) 94 95 for each in systems: 96 context.Error(each, 'there are no emitters emitting into this particle system', actions=[DeleteAction(context.GetStack()[0], each, True)]) 97 98 99 def _UnsetUsedByGpu(element, particleSystem, root, default): 100 101 def inner(): 102 element.usedByGPU = False 103 particleSystem.UpdateElementDeclaration() 104 emitters = blue.FindInterface(root, 'Tr2DynamicEmitter') 105 for each in emitters: 106 if each.particleSystem == particleSystem: 107 each.Rebind() 108 109 for each in blue.FindInterface(root, 'Tr2InstancedMesh'): 110 if each.instanceGeometryResource == particleSystem: 111 each.instanceGeometryResource = None 112 each.instanceGeometryResource = particleSystem 113 114 return True 115 116 return ('Unset usedByGPU flag', inner, default) 117 118 119 def _DeleteElement(element, particleSystem, root, default): 120 121 def inner(): 122 particleSystem.elements.remove(element) 123 particleSystem.UpdateElementDeclaration() 124 particleSystem.RebindConstraints() 125 emitters = blue.FindInterface(root, 'Tr2DynamicEmitter') + blue.FindInterface(root, 'EveChildParticleSphere') 126 for each in emitters: 127 if each.particleSystem == particleSystem: 128 for generator in each.generators: 129 if isinstance(generator, trinity.Tr2SphereShapeAttributeGenerator): 130 if element.elementType == trinity.PARTICLE_ELEMENT_TYPE.POSITION: 131 generator.controlPosition = False 132 break 133 elif element.elementType == trinity.PARTICLE_ELEMENT_TYPE.VELOCITY: 134 generator.controlVelocity = False 135 break 136 elif generator.elementType == element.elementType: 137 if element.elementType != trinity.PARTICLE_ELEMENT_TYPE.CUSTOM or generator.customName == element.customName: 138 each.generators.remove(generator) 139 break 140 141 if hasattr(each, 'Rebind'): 142 each.Rebind() 143 else: 144 each.Refresh() 145 146 for each in blue.FindInterface(root, 'Tr2InstancedMesh'): 147 if each.instanceGeometryResource == particleSystem: 148 each.instanceGeometryResource = None 149 each.instanceGeometryResource = particleSystem 150 151 return True 152 153 return ('Delete element', inner, default) 154 155 156 def _CheckShaderInputs(context, inputs, particleSystem, elementNames): 157 psToVs = {trinity.PARTICLE_ELEMENT_TYPE.LIFETIME: 3, 158 trinity.PARTICLE_ELEMENT_TYPE.POSITION: 0, 159 trinity.PARTICLE_ELEMENT_TYPE.VELOCITY: 2, 160 trinity.PARTICLE_ELEMENT_TYPE.MASS: 4, 161 trinity.PARTICLE_ELEMENT_TYPE.CUSTOM: 5} 162 vsToPs = {v:k for k, v in psToVs.items()} 163 for each in particleSystem.elements: 164 usage = psToVs[each.elementType] 165 usageIndex = 8 if each.elementType == 1 else each.usageIndex + 8 166 if (usage, usageIndex) in inputs: 167 inputs.remove((usage, usageIndex)) 168 elif each.usedByGPU: 169 isCustom = each.elementType == trinity.PARTICLE_ELEMENT_TYPE.CUSTOM 170 actions = [_UnsetUsedByGpu(each, particleSystem, context.GetStack()[0], not isCustom)] 171 if each.elementType not in [trinity.PARTICLE_ELEMENT_TYPE.POSITION, trinity.PARTICLE_ELEMENT_TYPE.VELOCITY, trinity.PARTICLE_ELEMENT_TYPE.LIFETIME]: 172 actions.append(_DeleteElement(each, particleSystem, context.GetStack()[0], isCustom)) 173 context.Error(each, 'element has usedByGPU set, but the shader does not use it', actions=actions) 174 175 for usage, usageIndex in inputs: 176 if usageIndex < 8: 177 continue 178 if usage in vsToPs: 179 try: 180 name = elementNames[vsToPs[usage], usageIndex - 8] 181 except KeyError: 182 name = trinity.PARTICLE_ELEMENT_TYPE.GetNameFromValue(vsToPs[usage]) 183 if vsToPs[usage] == trinity.PARTICLE_ELEMENT_TYPE.CUSTOM: 184 name += '/%s' % (usageIndex - 8) 185 186 else: 187 name = '(%s, %s)' % (usage, usageIndex - 8) 188 context.Error(particleSystem, 'the shader expects an %s element that is not present in the particle system' % name) 189 190 191 @Validate('/*') 192 def CheckShaderInputs(context, root): 193 ps = {} 194 elementNames = {} 195 for mesh in blue.FindInterface(root, 'Tr2InstancedMesh'): 196 if isinstance(mesh.instanceGeometryResource, trinity.Tr2ParticleSystem): 197 areas = [] 198 if mesh.opaqueAreas: 199 areas.extend(mesh.opaqueAreas) 200 if mesh.decalAreas: 201 areas.extend(mesh.decalAreas) 202 if mesh.depthAreas: 203 areas.extend(mesh.depthAreas) 204 if mesh.transparentAreas: 205 areas.extend(mesh.transparentAreas) 206 if mesh.additiveAreas: 207 areas.extend(mesh.additiveAreas) 208 if mesh.pickableAreas: 209 areas.extend(mesh.pickableAreas) 210 if mesh.mirrorAreas: 211 areas.extend(mesh.mirrorAreas) 212 if mesh.decalNormalAreas: 213 areas.extend(mesh.decalNormalAreas) 214 if mesh.depthNormalAreas: 215 areas.extend(mesh.depthNormalAreas) 216 if mesh.opaquePrepassAreas: 217 areas.extend(mesh.opaquePrepassAreas) 218 if mesh.decalPrepassAreas: 219 areas.extend(mesh.decalPrepassAreas) 220 if mesh.geometryEraserAreas: 221 areas.extend(mesh.geometryEraserAreas) 222 if mesh.distortionAreas: 223 areas.extend(mesh.distortionAreas) 224 inputs = set() 225 for each in areas: 226 if each.effect: 227 inputs.update(effects.GetVertexShaderInputs(each.effect, effectCache)) 228 for k, v in particlesystemelements.GetParticleElementAnnotations(each.effect, effectCache).items(): 229 if v.name: 230 elementNames[k] = v.name 231 232 ps.setdefault(mesh.instanceGeometryResource, set()).update(inputs) 233 234 for k, v in ps.items(): 235 _CheckShaderInputs(context, v, k, elementNames) 236 237 238 @Validate('Tr2InstancedMesh') 239 def NoCopyPasteBounds(context, mesh): 240 if mesh.boundsMethod != trinity.Tr2InstanceMeshBoundsMethod.STATIC: 241 return 242 if mesh.minBounds == (-10000000000.0, -10000000000.0, -10000000000.0) or mesh.maxBounds == (10000000000.0, 10000000000.0, 10000000000.0): 243 context.Error(mesh, 'invalid mesh bounds (copy-paste error?)')