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

paperDollSculpting.py (49807B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\paperDoll\paperDollSculpting.py
      2 from __future__ import print_function
      3 import trinity
      4 import blue
      5 import uthread
      6 import struct
      7 import math
      8 import geo2
      9 from carbon.client.script.animation.animationController import AnimationController
     10 import telemetry
     11 import legacy_r_drive
     12 from eve.common.script.paperDoll.paperDollCommonFunctions import Yield
     13 from .commonClientFunctions import MoveAreas
     14 from eve.common.script.paperDoll.paperDollDefinitions import BLENDSHAPE_CATEGORIES, BODY_BLENDSHAPE_ZONES, DOLL_EXTRA_PARTS, HEAD_BLENDSHAPE_ZONES, HEAD_CATEGORIES
     15 from eve.common.script.paperDoll.yamlPreloader import LoadYamlFileNicely
     16 
     17 class PaperDollSculpting:
     18 
     19     def GetProjectionAndViewMatrix(self):
     20         return (self.camera.projectionMatrix, self.camera.viewMatrix)
     21 
     22     def OnInvalidate(self, dev):
     23         self.Stop()
     24 
     25     def OnCreate(self, dev):
     26         self.PreloadZoneMaps(self.doll)
     27         self.Reset(self.doll, self.avatar, mode=self.mode, camera=self.camera, callback=self.callback, pickCallback=self.pickCallback, inactiveZones=self.inactiveZones)
     28         self.ready = True
     29 
     30     @telemetry.ZONE_METHOD
     31     def GetMaskAtPixel(self, x, y):
     32         self.zoneSize = (self.zoneMap.width, self.zoneMap.height)
     33         self.bodyZoneSize = (self.bodyZoneMap.width, self.bodyZoneMap.height)
     34         if self.zoneSize[0] == 0 or self.bodyZoneSize[0] == 0:
     35             return (0,
     36              0,
     37              [0, 0],
     38              0,
     39              True)
     40         mask = 0
     41         dev = trinity.device
     42         cameraProj, cameraView = self.GetProjectionAndViewMatrixFunc()
     43         if self.pickScene:
     44             self.pickAvatar.rotation = self.avatar.rotation
     45         backbuffer = self.backbuffer or trinity.device.GetRenderContext().GetDefaultBackBuffer()
     46         viewport = trinity.TriViewport(0, 0, backbuffer.width, backbuffer.height)
     47         pick = None
     48         if hasattr(self.pickScene, 'PickObject'):
     49             pick = self.pickScene.PickPointAndObject(x, y, cameraProj, cameraView, viewport)
     50         if not pick:
     51             return (0,
     52              0,
     53              [0, 0],
     54              0,
     55              True)
     56         if not hasattr(self.pickScene, 'PickObjectUV') or pick[0].__typename__ != 'Tr2IntSkinnedObject':
     57             return (0,
     58              pick[0],
     59              [0, 0],
     60              pick[1],
     61              True)
     62         pickedPixelUV = self.pickScene.PickObjectUV(x, y, cameraProj, cameraView, viewport)
     63         isHead = True
     64         self.pickedSide = 'right'
     65         midpoint = 0.5
     66         if pickedPixelUV[0] > 0.5:
     67             self.pickedMesh = 'head'
     68             pickedPixelUV = [(pickedPixelUV[0] - 0.5) * 2.0, pickedPixelUV[1] * 2.0]
     69             if pickedPixelUV[0] < 0.0 or pickedPixelUV[0] > 1.0 or pickedPixelUV[1] < 0.0 or pickedPixelUV[1] > 1.0:
     70                 return (0,
     71                  pick[0],
     72                  pickedPixelUV,
     73                  pick[1],
     74                  isHead)
     75             pixel = [int(pickedPixelUV[0] * self.zoneSize[0]), int(pickedPixelUV[1] * self.zoneSize[1])]
     76             buffer = self.zoneMap.GetRawData()
     77             color = struct.unpack_from('=BBBB', buffer[0], pixel[0] * 4 + pixel[1] * buffer[3])
     78             mask = color[0]
     79         else:
     80             isHead = False
     81             self.pickedMesh = 'body'
     82             pickedPixelUV = [pickedPixelUV[0] * 2.0, pickedPixelUV[1]]
     83             pixel = [int(pickedPixelUV[0] * self.bodyZoneSize[0]), int(pickedPixelUV[1] * self.bodyZoneSize[1])]
     84             buffer = self.bodyZoneMap.GetRawData()
     85             color = struct.unpack_from('=BBBB', buffer[0], pixel[0] * 4 + pixel[1] * buffer[3])
     86             mask = color[0]
     87             zone = self.ConvertMaskToZone(mask)
     88             if zone in (0, 2, 5) or zone == 1 and pickedPixelUV[0] < 0.37:
     89                 midpoint = 0.2
     90         if pickedPixelUV[0] > midpoint:
     91             self.pickedSide = 'left'
     92         return (mask,
     93          pick[0],
     94          pickedPixelUV,
     95          pick[1],
     96          isHead)
     97 
     98     def ConvertMaskToZone(self, mask):
     99         return int(mask / 10.0 - 1)
    100 
    101     def RightDown(self, x, y):
    102         self.rightClick = True
    103         self.PickWrapper(x, y)
    104 
    105     def RightUp(self, x, y):
    106         self.EndMotion(x, y)
    107 
    108     @telemetry.ZONE_METHOD
    109     def PickWrapper(self, x, y):
    110         xy = (x, y)
    111         dev = trinity.device
    112         self.markerCorrection = [0.0, 0.0]
    113         mask, pick, uv, xyz, isHead = self.GetMaskAtPixel(x, y)
    114         if mask > 0:
    115             if self.mode == 'sculpt':
    116                 self.avatar.animationUpdater.TogglePauseAnimations(True)
    117             self.startXY = [x, y]
    118             camera = self.camera
    119             cPos = camera.GetPosition()
    120             cInt = camera.GetPointOfInterest()
    121             avatarRotation = self.avatar.rotation
    122             vector = [cPos[0] - cInt[0], cPos[1] - cInt[1], cPos[2] - cInt[2]]
    123             invRotation = geo2.QuaternionInverse(avatarRotation)
    124             vector = geo2.QuaternionTransformVector(invRotation, vector)
    125             if abs(vector[0]) > abs(vector[2]):
    126                 if vector[0] > 0.0:
    127                     self.direction = 'right'
    128                 else:
    129                     self.direction = 'left'
    130             else:
    131                 self.direction = 'front'
    132             self.zone = self.ConvertMaskToZone(mask)
    133             if self.zone not in self.inactiveZones:
    134                 if self.pickCallback:
    135                     self.pickCallback(self.zone)
    136             if self.useHighlighting:
    137                 if self.highlightGhost.IsActive():
    138                     self.highlightGhost.Stop()
    139                 if self.bodyHighlightGhost.IsActive():
    140                     self.bodyHighlightGhost.Stop()
    141         else:
    142             self.zone = -1
    143             self.direction = None
    144             self.startXY = None
    145         self.rightClick = False
    146         return self.zone
    147 
    148     @telemetry.ZONE_METHOD
    149     def MotionWrapper(self, x, y):
    150         if self.handlingMotion:
    151             return
    152         if self.stopInteraction:
    153             return
    154         currentFrameCounter = trinity.GetCurrentFrameCounter()
    155         if self.lastFrameSculpted == currentFrameCounter:
    156             return
    157         self.lastFrameSculpted = currentFrameCounter
    158         self.handlingMotion = True
    159         if self.startXY:
    160             delta = [self.startXY[0] - x, self.startXY[1] - y]
    161             if sum(delta) == 0:
    162                 self.handlingMotion = False
    163                 return
    164             if self.mode == 'sculpt':
    165                 result = self.UpdateField(delta)
    166                 self.UpdateBlendShapes(result)
    167             elif self.mode == 'animation':
    168                 result = self.UpdateFieldAnimation(delta)
    169                 if result:
    170                     self.UpdateAnimation(result)
    171         else:
    172             mask, pick, uv, xyz, isHead = self.GetMaskAtPixel(x, y)
    173             zone = self.ConvertMaskToZone(mask)
    174             isFront = True
    175             camera = self.camera
    176             cPos = camera.GetPosition()
    177             cInt = camera.GetPointOfInterest()
    178             vector = [cPos[0] - cInt[0], cPos[1] - cInt[1], cPos[2] - cInt[2]]
    179             if abs(vector[0]) > abs(vector[2]):
    180                 isFront = False
    181             if zone not in self.inactiveZones:
    182                 if self.callback:
    183                     self.callback(zone, isFront, isHead)
    184             if self.useHighlighting:
    185                 if self.highlightGhost.IsActive() and not isHead:
    186                     self.highlightGhost.Stop()
    187                 if self.bodyHighlightGhost.IsActive() and isHead:
    188                     self.bodyHighlightGhost.Stop()
    189                 if mask == 0:
    190                     if self.highlightGhost.IsActive() and not self.previewingHighlight:
    191                         self.highlightGhost.Stop()
    192                     if self.bodyHighlightGhost.IsActive() and not self.previewingHighlight:
    193                         self.bodyHighlightGhost.Stop()
    194                     self.handlingMotion = False
    195                     return
    196                 if not self.highlightGhost.IsActive() and isHead and not self.mode == 'bodyselect':
    197                     uthread.new(self.highlightGhost.Start, pick)
    198                 if not self.bodyHighlightGhost.IsActive() and not isHead and (self.mode == 'sculpt' or self.mode == 'bodyselect'):
    199                     uthread.new(self.bodyHighlightGhost.Start, pick)
    200                 if isHead:
    201                     if self.highlightGhost.IsActive():
    202                         if not self.overrideMousePos:
    203                             self.highlightGhost.SetMousePos(xyz, 0.05)
    204                         else:
    205                             self.highlightGhost.SetMousePos(self.overrideMousePosXYZ, 0.05)
    206                         if zone not in self.inactiveZones:
    207                             self.KillHighlightPreview()
    208                             self.highlightGhost.SetActiveZoneColor((1.0,
    209                              1.0,
    210                              1.0,
    211                              mask / 255.0))
    212                 elif self.bodyHighlightGhost.IsActive():
    213                     if not self.overrideMousePos:
    214                         self.highlightGhost.SetMousePos(xyz, 0.15)
    215                     else:
    216                         self.highlightGhost.SetMousePos(self.overrideMousePosXYZ, 0.15)
    217                     if zone not in self.inactiveZones:
    218                         self.KillHighlightPreview()
    219                         self.bodyHighlightGhost.SetActiveZoneColor((1.0,
    220                          1.0,
    221                          1.0,
    222                          mask / 255.0))
    223         self.handlingMotion = False
    224 
    225     @telemetry.ZONE_METHOD
    226     def OverrideMousePosWithJoint(self, joint_name):
    227         bpos = (0.0, 0.0, 0.0)
    228         if self.avatar is not None and self.avatar.animationUpdater is not None:
    229             joint = self.avatar.GetBoneIndex(joint_name)
    230             if joint == 4294967295L:
    231                 bpos = (0.0, 0.0, 0.0)
    232             else:
    233                 bpos = self.avatar.GetBonePosition(joint)
    234         self.OverrideMousePos(bpos[0], bpos[1], bpos[2])
    235 
    236     @telemetry.ZONE_METHOD
    237     def OverrideMousePos(self, x, y, z):
    238         self.overrideMousePos = True
    239         self.overrideMousePosXYZ = (x, y, z)
    240 
    241     @telemetry.ZONE_METHOD
    242     def ReleaseOverrideMousePos(self):
    243         self.overrideMousePos = False
    244         self.overrideMousePosXYZ = (0.0, 0.0, 0.0)
    245 
    246     @telemetry.ZONE_METHOD
    247     def HighlightPreview(self, isMale = True):
    248         import time
    249         self.previewingHighlight = True
    250         start = time.time()
    251         self.highlightGhost.Start(self.avatar)
    252         self.bodyHighlightGhost.Start(self.avatar)
    253         if self.mode == 'sculpt':
    254             sequence = [10.1,
    255              11.1,
    256              17,
    257              13,
    258              1,
    259              15,
    260              2,
    261              3,
    262              4,
    263              12,
    264              18,
    265              5,
    266              14.1,
    267              7,
    268              9.1,
    269              8,
    270              6]
    271             for i, each in enumerate(sequence):
    272                 self.highlightGhost.SetActiveZoneColor((2.0,
    273                  2.0,
    274                  2.0,
    275                  each * 10 / 255.0))
    276                 while time.time() < start + (i + 1) / 3.0:
    277                     Yield(frameNice=False)
    278 
    279         else:
    280             numZones = 0
    281             if self.mode == 'hair':
    282                 numZones = 2
    283                 if isMale:
    284                     numZones = 3
    285             elif self.mode == 'bodyselect':
    286                 numZones = 5
    287             elif self.mode == 'makeup':
    288                 numZones = 3
    289             for i in range(numZones + 1):
    290                 if self.mode == 'bodyselect':
    291                     self.bodyHighlightGhost.SetActiveZoneColor((1.0,
    292                      1.0,
    293                      1.0,
    294                      i * 10 / 255.0))
    295                 else:
    296                     self.highlightGhost.SetActiveZoneColor((2.0,
    297                      2.0,
    298                      2.0,
    299                      i * 10 / 255.0))
    300                 while time.time() < start + i / 3.0:
    301                     Yield(frameNice=False)
    302 
    303         self.highlightGhost.Stop()
    304         self.bodyHighlightGhost.Stop()
    305         self.previewingHighlight = False
    306 
    307     def KillHighlightPreview(self):
    308         self.previewingHighlight = False
    309         if self.highlightThread is not None:
    310             self.highlightThread.kill()
    311 
    312     def RunHighlightPreview(self, isMale = True):
    313         self.KillHighlightPreview()
    314         if self.useHighlighting:
    315             self.highlightThread = uthread.new(self.HighlightPreview, isMale)
    316 
    317     @telemetry.ZONE_METHOD
    318     def EndMotion(self, x, y):
    319         self.stopInteraction = False
    320         if self.mode == 'sculpt':
    321             self.avatar.animationUpdater.TogglePauseAnimations(False)
    322         for f in self.fieldResetData:
    323             pos = self.fieldResetData[f]
    324             field = None
    325             if self.mode == 'sculpt':
    326                 if f in self.fieldData['Fields']:
    327                     field = self.fieldData['Fields'][f]
    328             elif self.mode == 'animation':
    329                 if f in self.fieldDataAnimation['Fields']:
    330                     field = self.fieldDataAnimation['Fields'][f]
    331             if field:
    332                 field['MarkerPosition'] = pos
    333 
    334         self.fieldResetData = {}
    335         self.startXY = None
    336 
    337     @telemetry.ZONE_METHOD
    338     def UpdateAnimation(self, values):
    339         doll = self.doll
    340         av = self.avatar
    341         if self.animationController is None or self.animationController.animationNetwork.avatar != av:
    342             self.animationController = AnimationController(av)
    343         for v in values:
    344             if v.endswith('_X') or v.endswith('_Y') or v.endswith('_Z'):
    345                 baseName = v[:-2]
    346                 self.animationController.SetControlParameter(baseName, (values[baseName + '_X'], values[baseName + '_Y'], values[baseName + '_Z']))
    347                 break
    348             rightVersion = v.replace('Left', 'Right')
    349             if rightVersion != v:
    350                 if self.poseSymmetric:
    351                     self.animationController.SetControlParameter(v, values[v])
    352                     self.animationController.SetControlParameter(rightVersion, values[v])
    353                 elif self.pickedSide == 'left':
    354                     self.animationController.SetControlParameter(v, values[v])
    355                 else:
    356                     self.animationController.SetControlParameter(rightVersion, values[v])
    357             else:
    358                 self.animationController.SetControlParameter(v, values[v])
    359 
    360     @telemetry.ZONE_METHOD
    361     def UpdateBlendShapes(self, values, doUpdate = True):
    362         doll = self.doll
    363         mods = []
    364         for blendshapeCategory in BLENDSHAPE_CATEGORIES:
    365             modifiersByCategory = doll.buildDataManager.GetModifiersByCategory(blendshapeCategory)
    366             mods.extend(modifiersByCategory)
    367 
    368         cleanValues = {}
    369         valueThreshold = 0.0001
    370         if doll:
    371             av = self.avatar
    372             for v in iter(values):
    373                 mod = (v + 'shape').lower()
    374                 cleanValues[mod] = values[v]
    375                 limit = doll.buildDataManager.modifierLimits.get(mod)
    376                 if limit:
    377                     if cleanValues[mod] > limit[1]:
    378                         cleanValues[mod] = limit[1]
    379                     elif cleanValues[mod] < limit[0]:
    380                         cleanValues[mod] = limit[0]
    381                 foundMod = False
    382                 for m in iter(mods):
    383                     if mod == m.name:
    384                         m.weight = values[v]
    385                         foundMod = True
    386                         if values[v] < valueThreshold:
    387                             resPath = '{0}/{1}'.format(m.categorie, m.name)
    388                             doll.RemoveResource(resPath, self.factory)
    389                         break
    390 
    391                 if not foundMod:
    392                     for zone in BODY_BLENDSHAPE_ZONES:
    393                         if zone.lower() in mod and cleanValues[mod] > valueThreshold:
    394                             resPath = '{0}/{1}'.format(DOLL_EXTRA_PARTS.BODYSHAPES, mod)
    395                             doll.AddResource(resPath, cleanValues[mod], self.factory)
    396                             foundMod = True
    397                             break
    398 
    399                 if not foundMod:
    400                     for zone in HEAD_BLENDSHAPE_ZONES:
    401                         if zone.lower() in mod and cleanValues[mod] > valueThreshold:
    402                             resPath = '{0}/{1}'.format(HEAD_CATEGORIES.FACEMODIFIERS, mod)
    403                             doll.AddResource(resPath, cleanValues[mod], self.factory)
    404                             break
    405 
    406             if doUpdate:
    407                 doll.Update(self.factory, avatar=av)
    408             for m in mods:
    409                 if m.weight > 0 and m not in cleanValues:
    410                     limit = doll.buildDataManager.modifierLimits.get(m.name)
    411                     cleanValues[m.name] = m.weight
    412                     if limit:
    413                         if cleanValues[m.name] > limit[1]:
    414                             cleanValues[m.name] = limit[1]
    415                         elif cleanValues[m.name] < limit[0]:
    416                             cleanValues[m.name] = limit[0]
    417 
    418         pickAvatar = self.pickAvatar
    419         if pickAvatar:
    420             self.UpdatePickBlendshapes(pickAvatar, cleanValues)
    421 
    422     @telemetry.ZONE_METHOD
    423     def UpdatePickBlendshapes(self, avatar, values):
    424         meshGeometryResPaths = {}
    425         modifierList = self.doll.buildDataManager.GetSortedModifiers()
    426         for m in self.pickExtraMods:
    427             modifierList.append(m)
    428 
    429         for modifier in iter(modifierList):
    430             meshGeometryResPaths.update(modifier.meshGeometryResPaths)
    431 
    432         meshGeometryResPaths.update(self.pickAvatarPaths)
    433         meshes = avatar.Find('Trinity.Tr2Mesh')
    434         uthread.new(self.factory.ApplyMorphTargetsToMeshes, meshes, values, self.pickAvatarCache, meshGeometryResPaths)
    435 
    436     @telemetry.ZONE_METHOD
    437     def LoadMapping(self):
    438         self.mapping = {}
    439         self.mapping[0] = 'Nose'
    440         self.mapping[1] = 'NoseTip'
    441         self.mapping[2] = 'Nostrils'
    442         self.mapping[3] = 'CheeksUpper'
    443         self.mapping[4] = 'Jaw'
    444         self.mapping[5] = 'Chin'
    445         self.mapping[6] = 'MouthCorner'
    446         self.mapping[7] = 'LowerLip'
    447         self.mapping[8] = 'UpperLip'
    448         self.mapping[9] = 'InnerBrow'
    449         self.mapping[10] = 'OuterBrow'
    450         self.mapping[11] = 'Ears'
    451         self.mapping[12] = 'Eyes'
    452         self.mapping[13] = 'CheeksMiddle'
    453         self.mapping[14] = 'Nosebone'
    454         self.mapping[15] = 'EyesInner'
    455         self.mapping[16] = 'EyesOuter'
    456         self.mapping[17] = 'JawCheek'
    457         self.bodyMapping = {}
    458         self.bodyMapping[0] = 'Legs'
    459         self.bodyMapping[1] = 'Abs'
    460         self.bodyMapping[2] = 'Pelvis'
    461         self.bodyMapping[3] = 'Shoulders'
    462         self.bodyMapping[4] = 'Chest'
    463         self.bodyMapping[5] = 'Feet'
    464         self.bodyMapping[6] = 'Neck'
    465 
    466     def OnSculptFace(self, evt):
    467         self.mode = 'sculpt'
    468 
    469     def OnPoseFace(self, evt):
    470         self.mode = 'animation'
    471         self.poseSymmetric = True
    472 
    473     def OnPoseAsymFace(self, evt):
    474         self.mode = 'animation'
    475         self.poseSymmetric = False
    476 
    477     @telemetry.ZONE_METHOD
    478     def __init__(self, avatar = None, doll = None, scene = None, camera = None, factory = None, callback = None, viewProjCallback = None, mode = 'sculpt', pickCallback = None, inactiveZones = [], backbuffer = None):
    479         self.avatar = avatar
    480         self.doll = doll
    481         self.scene = scene
    482         self.head = ''
    483         self.gender = ''
    484         self.pickScene = None
    485         self.pickAvatar = None
    486         self.pickExtraMods = []
    487         self.pickAvatarCache = {}
    488         self.pickAvatarPaths = {}
    489         self.updateRenderjob = None
    490         self.backbuffer = backbuffer
    491         self.ready = False
    492         if camera:
    493             self.camera = camera
    494             self.GetProjectionAndViewMatrixFunc = self.GetProjectionAndViewMatrix
    495         if viewProjCallback:
    496             self.GetProjectionAndViewMatrixFunc = viewProjCallback
    497         self.factory = factory
    498         self.callback = callback
    499         self.pickCallback = pickCallback
    500         self.inactiveZones = inactiveZones
    501         self.keysDown = set()
    502         self.handlingMotion = False
    503         self.lastFrameSculpted = 0
    504         self.rightClick = False
    505         self.mode = None
    506         self.startXY = None
    507         self.direction = None
    508         self.pickedSide = None
    509         self.pickedMesh = 'head'
    510         self.zone = -1
    511         self.mapping = {}
    512         self.fieldData = None
    513         self.fieldDataAnimation = None
    514         self.stopInteraction = False
    515         self.finalAttrs = {}
    516         self.fieldResetData = {}
    517         self.fieldLKG = {}
    518         self.fieldLKGPos = {}
    519         self.markerCorrection = [0.0, 0.0]
    520         self.dirDict = {}
    521         self.LoadMapping()
    522         self.LoadFieldData()
    523         self.FillInFieldData()
    524         self.lightSettings = []
    525         self.poseSymmetric = False
    526         self.useHighlighting = True
    527         self.previewingHighlight = False
    528         self.highlightGhost = None
    529         self.highlightThread = None
    530         self.overrideMousePos = False
    531         self.overrideMousePosXYZ = (0.0, 0.0, 0.0)
    532         self.animationController = None
    533         self.PreloadZoneMaps(doll)
    534         self.Reset(doll, avatar, mode=mode, camera=camera, callback=callback, pickCallback=pickCallback, inactiveZones=inactiveZones)
    535         self.ready = True
    536         trinity.device.RegisterResource(self)
    537 
    538     @telemetry.ZONE_METHOD
    539     def PreloadZoneMaps(self, doll):
    540         textureRoot = 'R:/'
    541         textureSuffix = '.tga'
    542         if not legacy_r_drive.loadFromContent:
    543             textureRoot = 'res:/'
    544             textureSuffix = '.png'
    545         self.zoneMaps = {}
    546         self.highlightMaps = {}
    547         for each in ('hair', 'makeup', 'bodyselect', 'animation', 'default'):
    548             zonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/SculptZones' + textureSuffix
    549             headGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/male_head_fx' + textureSuffix
    550             bodyGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/male_body_fx' + textureSuffix
    551             if doll.gender == 'female':
    552                 headGridPath = headGridPath.replace('male_', 'female_')
    553                 bodyGridPath = bodyGridPath.replace('male_', 'female_')
    554             if headGridPath not in self.highlightMaps:
    555                 self.highlightMaps[headGridPath] = blue.resMan.GetResource(headGridPath)
    556             if bodyGridPath not in self.highlightMaps:
    557                 self.highlightMaps[bodyGridPath] = blue.resMan.GetResource(bodyGridPath)
    558             bodyZonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/BodySculptZones' + textureSuffix
    559             headTiling = 1
    560             bodyTiling = 1
    561             if each == 'hair':
    562                 zonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/HairZones' + textureSuffix
    563                 bodyZonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/EmptyZone' + textureSuffix
    564                 headGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    565                 bodyGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    566                 headTiling = 40
    567                 bodyTiling = 40
    568             elif each == 'makeup':
    569                 zonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/MakeupZones' + textureSuffix
    570                 bodyZonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/EmptyZone' + textureSuffix
    571                 headGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    572                 bodyGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    573                 headTiling = 40
    574                 bodyTiling = 40
    575             elif each == 'bodyselect':
    576                 zonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/EmptyZone' + textureSuffix
    577                 bodyZonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/BodySelectZones' + textureSuffix
    578                 headGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    579                 bodyGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/SelectGrid' + textureSuffix
    580                 headTiling = 40
    581                 bodyTiling = 40
    582             elif each == 'animation':
    583                 zonePath = textureRoot + 'Graphics/Character/Global/FaceSetup/AnimZones' + textureSuffix
    584                 headGridPath = textureRoot + 'Graphics/Character/Global/FaceSetup/male_head_Profile_fx' + textureSuffix
    585                 if doll.gender == 'female':
    586                     headGridPath = headGridPath.replace('male_', 'female_')
    587                 headTiling = 1
    588             zoneMap = trinity.Tr2HostBitmap()
    589             zoneMap.CreateFromFile(zonePath)
    590             bodyZoneMap = trinity.Tr2HostBitmap()
    591             bodyZoneMap.CreateFromFile(bodyZonePath)
    592             zoneSize = (0, 0)
    593             bodyZoneSize = (0, 0)
    594             self.zoneMaps[each] = (zonePath,
    595              headGridPath,
    596              bodyGridPath,
    597              headTiling,
    598              bodyTiling,
    599              bodyZonePath,
    600              zoneMap,
    601              zoneSize,
    602              bodyZoneMap,
    603              bodyZoneSize)
    604 
    605     def IsReady(self):
    606         return self.ready
    607 
    608     def IsDollDifferent(self, doll, avatar):
    609         retVal = False
    610         if avatar != self.avatar:
    611             self.avatar = avatar
    612             retVal = True
    613         head = doll.buildDataManager.GetModifiersByCategory('head')
    614         if not head == self.head:
    615             self.head = head
    616             retVal = True
    617         if not self.gender == doll.gender:
    618             self.gender = doll.gender
    619             retVal = True
    620         return retVal
    621 
    622     @telemetry.ZONE_METHOD
    623     def Reset(self, doll, avatar, camera = None, mode = 'sculpt', callback = None, pickCallback = None, inactiveZones = [], skipPickSceneReset = False):
    624         dollUpdated = self.IsDollDifferent(doll, avatar)
    625         self.doll = doll
    626         if self.pickAvatar:
    627             self.pickAvatar.animationUpdater = avatar.animationUpdater
    628         if self.mode == mode and not dollUpdated:
    629             return
    630         if camera is not None:
    631             self.camera = camera
    632         if (dollUpdated or not self.pickScene or mode != self.mode) and not skipPickSceneReset:
    633             self.SetupPickScene()
    634         self.avatar = avatar
    635         self.mode = mode
    636         self.inactiveZones = inactiveZones
    637         if pickCallback:
    638             self.pickCallback = pickCallback
    639         if callback:
    640             self.callback = callback
    641         if mode in self.zoneMaps:
    642             zonePath, headGridPath, bodyGridPath, headTiling, bodyTiling, bodyZonePath, self.zoneMap, self.zoneSize, self.bodyZoneMap, self.bodyZoneSize = self.zoneMaps[mode]
    643         else:
    644             zonePath, headGridPath, bodyGridPath, headTiling, bodyTiling, bodyZonePath, self.zoneMap, self.zoneSize, self.bodyZoneMap, self.bodyZoneSize = self.zoneMaps['default']
    645         self.zoneSize = (self.zoneMap.width, self.zoneMap.height)
    646         self.bodyZoneSize = (self.bodyZoneMap.width, self.bodyZoneMap.height)
    647         if self.useHighlighting:
    648             from eve.client.script.paperDoll.PaperdollSculptingGhost import PaperdollSculptingGhost
    649             self.highlightGhost = PaperdollSculptingGhost(zonePath=zonePath, texturePath=headGridPath, noisePath='res:/Texture/Global/noise.png', meshFilter=['head'], tiling=headTiling)
    650             self.bodyHighlightGhost = PaperdollSculptingGhost(zonePath=bodyZonePath, texturePath=bodyGridPath, noisePath='res:/Texture/Global/noise.png', meshFilter=['topinner',
    651              'bottominner',
    652              'hands',
    653              'feet',
    654              'dependantsnude'], ignoreZ=True, tiling=bodyTiling)
    655 
    656     @telemetry.ZONE_METHOD
    657     def Stop(self):
    658         self.mode = None
    659         if self.updateRenderjob:
    660             self.updateRenderjob.UnscheduleRecurring()
    661         self.updateRenderjob = None
    662         self.pickScene = None
    663         self.pickAvatar = None
    664         self.pickExtraMods = []
    665         self.pickAvatarCache = {}
    666 
    667     @telemetry.ZONE_METHOD
    668     def SetupPickScene(self, doUpdate = True):
    669         self.pickScene = trinity.Tr2InteriorScene()
    670         if hasattr(self.pickScene, 'updateShadowCubeMap'):
    671             self.pickScene.updateShadowCubeMap = False
    672         self.pickAvatarCache = {}
    673         self.pickAvatar = self.avatar.CopyTo()
    674         foundUpperNude = False
    675         foundLowerNude = False
    676         foundHead = False
    677         torsoClickable = False
    678         for meshIx in range(len(self.avatar.visualModel.meshes)):
    679             mesh = self.avatar.visualModel.meshes[meshIx]
    680             self.pickAvatar.visualModel.meshes[meshIx].SetGeometryRes(mesh.geometry)
    681             if mesh.name.startswith('topinner'):
    682                 foundUpperNude = True
    683                 if len(mesh.opaqueAreas):
    684                     torsoClickable |= True
    685             if mesh.name.startswith('bottominner'):
    686                 foundLowerNude = True
    687             if mesh.name.startswith('head'):
    688                 foundHead = True
    689 
    690         deleteList = []
    691         for mesh in self.pickAvatar.visualModel.meshes:
    692             if mesh.name.startswith('feet') or mesh.name.startswith('hands'):
    693                 deleteList.append(mesh)
    694 
    695         for d in deleteList:
    696             self.pickAvatar.visualModel.meshes.remove(d)
    697 
    698         self.pickExtraMods = []
    699         if not foundUpperNude or not torsoClickable:
    700             deleteList = []
    701             for mesh in self.pickAvatar.visualModel.meshes:
    702                 if mesh.name.startswith('topinner'):
    703                     deleteList.append(mesh)
    704 
    705             for d in deleteList:
    706                 self.pickAvatar.visualModel.meshes.remove(d)
    707 
    708             torsoMod = self.factory.CollectBuildData(self.doll.gender, 'topinner/torso_nude')
    709             self.pickExtraMods.append(torsoMod)
    710             item = blue.resMan.LoadObject(torsoMod.redfile)
    711             if item:
    712                 index = 1
    713                 for m in item.meshes:
    714                     m.name = 'topinner' + str(index)
    715                     self.pickAvatar.visualModel.meshes.append(m)
    716                     torsoMod.meshGeometryResPaths[m.name] = m.geometryResPath
    717                     self.pickAvatarPaths[m.name] = m.geometryResPath
    718                     index += 1
    719 
    720         index = 1
    721         for armPart in ['dependants/sleeveslower/standard', 'dependants/sleevesupper/standard']:
    722             armMod = self.factory.CollectBuildData(self.doll.gender, armPart)
    723             self.pickExtraMods.append(armMod)
    724             item = blue.resMan.LoadObject(armMod.redfile)
    725             if item:
    726                 for m in item.meshes:
    727                     m.name = 'dependantsnude' + str(index)
    728                     self.pickAvatar.visualModel.meshes.append(m)
    729                     armMod.meshGeometryResPaths[m.name] = m.geometryResPath
    730                     self.pickAvatarPaths[m.name] = m.geometryResPath
    731                     index += 1
    732 
    733         if not foundLowerNude:
    734             legMod = self.factory.CollectBuildData(self.doll.gender, 'bottominner/legs_nude')
    735             self.pickExtraMods.append(legMod)
    736             item = blue.resMan.LoadObject(legMod.redfile)
    737             if item:
    738                 index = 1
    739                 for m in item.meshes:
    740                     m.name = 'bottominner' + str(index)
    741                     self.pickAvatar.visualModel.meshes.append(m)
    742                     legMod.meshGeometryResPaths[m.name] = m.geometryResPath
    743                     self.pickAvatarPaths[m.name] = m.geometryResPath
    744                     index += 1
    745 
    746         if not foundHead:
    747             headMod = self.factory.CollectBuildData(self.doll.gender, 'head/head_generic')
    748             self.pickExtraMods.append(headMod)
    749             item = blue.resMan.LoadObject(headMod.redfile)
    750             if item:
    751                 index = 1
    752                 for m in item.meshes:
    753                     m.name = 'head' + str(index)
    754                     self.pickAvatar.visualModel.meshes.append(m)
    755                     headMod.meshGeometryResPaths[m.name] = m.geometryResPath
    756                     self.pickAvatarPaths[m.name] = m.geometryResPath
    757                     index += 1
    758 
    759         handMod = self.factory.CollectBuildData(self.doll.gender, 'hands/hands_nude')
    760         self.pickExtraMods.append(handMod)
    761         item = blue.resMan.LoadObject(handMod.redfile)
    762         if item:
    763             index = 1
    764             for m in item.meshes:
    765                 m.name = 'hands' + str(index)
    766                 self.pickAvatar.visualModel.meshes.append(m)
    767                 handMod.meshGeometryResPaths[m.name] = m.geometryResPath
    768                 self.pickAvatarPaths[m.name] = m.geometryResPath
    769                 index += 1
    770 
    771         feetMod = self.factory.CollectBuildData(self.doll.gender, 'feet/feet_nude')
    772         self.pickExtraMods.append(feetMod)
    773         item = blue.resMan.LoadObject(feetMod.redfile)
    774         if item:
    775             index = 1
    776             for m in item.meshes:
    777                 m.name = 'feet' + str(index)
    778                 self.pickAvatar.visualModel.meshes.append(m)
    779                 feetMod.meshGeometryResPaths[m.name] = m.geometryResPath
    780                 self.pickAvatarPaths[m.name] = m.geometryResPath
    781                 index += 1
    782 
    783         for mesh in self.pickAvatar.visualModel.meshes:
    784             MoveAreas(mesh.decalAreas, mesh.opaqueAreas)
    785 
    786         self.pickAvatar.animationUpdater = self.avatar.animationUpdater
    787         self.pickAvatar.worldTransformUpdater = self.avatar.worldTransformUpdater
    788 
    789         def Filter(mesh):
    790             for f in ['head',
    791              'topinner',
    792              'bottominner',
    793              'hands',
    794              'feet',
    795              'dependantsnude']:
    796                 if mesh.name.lower().startswith(f):
    797                     return True
    798 
    799             return False
    800 
    801         index = 0
    802         remList = []
    803         for mesh in self.pickAvatar.visualModel.meshes:
    804             if not Filter(mesh):
    805                 remList.append(mesh)
    806 
    807         for m in remList:
    808             self.pickAvatar.visualModel.meshes.remove(m)
    809 
    810         self.pickScene.AddDynamic(self.pickAvatar)
    811         self.pickScene.RebuildSceneData()
    812         cameraProj, cameraView = self.GetProjectionAndViewMatrixFunc()
    813         rj = trinity.CreateRenderJob('ProdPickScene')
    814         RT = trinity.Tr2RenderTarget(32, 32, 1, trinity.PIXEL_FORMAT.B8G8R8A8_UNORM)
    815         depth = trinity.Tr2DepthStencil(32, 32, trinity.DEPTH_STENCIL_FORMAT.D24S8)
    816         rj.PushRenderTarget(RT)
    817         rj.PushDepthStencil(depth)
    818         rj.SetStdRndStates(trinity.RM_PICKING)
    819         rj.SetView(cameraView)
    820         rj.SetProjection(cameraProj)
    821         rj.Update(self.pickScene)
    822         rj.RenderScene(self.pickScene)
    823         rj.PopDepthStencil()
    824         rj.PopRenderTarget()
    825         rj.ScheduleOnce()
    826         trinity.renderJobs.UnscheduleByName('PickSceneUpdate')
    827         self.updateRenderjob = trinity.CreateRenderJob('PickSceneUpdate')
    828         self.updateRenderjob.SetView(cameraView)
    829         self.updateRenderjob.SetProjection(cameraProj)
    830         self.updateRenderjob.Update(self.pickScene)
    831         self.updateRenderjob.ScheduleRecurring()
    832         self.UpdateBlendShapes([], doUpdate=doUpdate)
    833 
    834     def KeyUpWrapper(self, key):
    835         self.keysDown.discard(key)
    836 
    837     def KeyDownWrapper(self, key):
    838         self.keysDown.add(key)
    839 
    840     @telemetry.ZONE_METHOD
    841     def LoadFieldData(self):
    842         self.fieldData = LoadYamlFileNicely('res:/Graphics/Character/Global/FaceSetup/TriangleFields.trif')
    843         self.fieldDataAnimation = LoadYamlFileNicely('res:/Graphics/Character/Global/FaceSetup/TriangleFieldsAnimation.trif')
    844 
    845     @telemetry.ZONE_METHOD
    846     def FillInFieldData(self):
    847         defaultFields = LoadYamlFileNicely('res:/Graphics/Character/Global/FaceSetup/DefaultFields.trif')
    848         import copy
    849 
    850         def FillInFields(mapping):
    851             for m in mapping:
    852                 for var in ['Front', 'Side']:
    853                     fieldName = mapping[m] + '_' + var
    854                     if fieldName not in self.fieldData['Fields']:
    855                         defaultField = copy.deepcopy(defaultFields['Fields']['Default_' + var])
    856                         newAttr = []
    857                         for a in defaultField['Attributes']:
    858                             newAttr.append(a.replace('XXX', mapping[m]))
    859 
    860                         defaultField['Attributes'] = newAttr
    861                         for v in defaultField['VertData']:
    862                             for pair in defaultField['VertData'][v]:
    863                                 pair[0] = pair[0].replace('XXX', mapping[m])
    864 
    865                         if False:
    866                             print('New Field:')
    867                             print(defaultField)
    868                             print('-----------------------------------')
    869                         self.fieldData['Fields'][fieldName] = defaultField
    870 
    871         FillInFields(self.mapping)
    872         FillInFields(self.bodyMapping)
    873 
    874     @telemetry.ZONE_METHOD
    875     def CalcBaryCentric2DTriangle(self, corners, position):
    876         x = [position[0],
    877          corners[0][0],
    878          corners[1][0],
    879          corners[2][0]]
    880         y = [position[1],
    881          corners[0][1],
    882          corners[1][1],
    883          corners[2][1]]
    884         b0 = (x[2] - x[1]) * (y[3] - y[1]) - (x[3] - x[1]) * (y[2] - y[1])
    885         b1 = ((x[2] - x[0]) * (y[3] - y[0]) - (x[3] - x[0]) * (y[2] - y[0])) / b0
    886         b2 = ((x[3] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[3] - y[0])) / b0
    887         b3 = ((x[1] - x[0]) * (y[2] - y[0]) - (x[2] - x[0]) * (y[1] - y[0])) / b0
    888         return (b1, b2, b3)
    889 
    890     @telemetry.ZONE_METHOD
    891     def ProjectToTriangle(self, p, a, b, c):
    892         import geo2
    893         ab = geo2.Vec3Subtract(b, a)
    894         ac = geo2.Vec3Subtract(c, a)
    895         ap = geo2.Vec3Subtract(p, a)
    896         d1 = geo2.Vec3Dot(ab, ap)
    897         d2 = geo2.Vec3Dot(ac, ap)
    898         if d1 <= 0 and d2 <= 0:
    899             return (a, (1, 0, 0))
    900         bp = geo2.Vec3Subtract(p, b)
    901         d3 = geo2.Vec3Dot(ab, bp)
    902         d4 = geo2.Vec3Dot(ac, bp)
    903         if d3 >= 0 and d4 <= d3:
    904             return (b, (0, 1, 0))
    905         vc = d1 * d4 - d3 * d2
    906         if vc <= 0 and d1 >= 0 and d3 <= 0:
    907             v = d1 / (d1 - d3)
    908             vab = geo2.Vec3Scale(ab, v)
    909             return (geo2.Vec3Add(a, vab), (1.0 - v, v, 0))
    910         cp = geo2.Vec3Subtract(p, c)
    911         d5 = geo2.Vec3Dot(ab, cp)
    912         d6 = geo2.Vec3Dot(ac, cp)
    913         if d6 >= 0 and d5 <= d6:
    914             return (c, (0, 0, 1))
    915         vb = d5 * d2 - d1 * d6
    916         if vb <= 0 and d2 >= 0 and d6 <= 0:
    917             w = d2 / (d2 - d6)
    918             wac = geo2.Vec3Scale(ac, w)
    919             return (geo2.Vec3Add(a, wac), (1.0 - w, 0, w))
    920         va = d3 * d6 - d5 * d4
    921         if va <= 0 and d4 - d3 >= 0 and d5 - d6 >= 0:
    922             w = (d4 - d3) / (d4 - d3 + (d5 - d6))
    923             bc = geo2.Vec3Subtract(c, b)
    924             wcb = geo2.Vec3Scale(bc, w)
    925             return (geo2.Vec3Add(b, wcb), (0, 1.0 - w, w))
    926         denom = 1.0 / (va + vb + vc)
    927         v = vb * denom
    928         w = vc * denom
    929         abv = geo2.Vec3Scale(ab, v)
    930         acw = geo2.Vec3Scale(ac, w)
    931         r = geo2.Vec3Add(a, abv)
    932         r = geo2.Vec3Add(r, acw)
    933         return (r, (1.0 - v - w, v, w))
    934 
    935     @telemetry.ZONE_METHOD
    936     def CalculateClosestPosInField(self, position, field):
    937 
    938         def CalcDistance(v1, v2):
    939             val = math.sqrt(pow(v1[0] - v2[0], 2) + pow(v1[1] - v2[1], 2))
    940             return val
    941 
    942         pos = [position[0], position[2]]
    943         ret = None
    944         shortestDistance = 1000000.0
    945         for t in field['Tris']:
    946             internalPos = self.ProjectToTriangle(pos, t[0][1], t[1][1], t[2][1])[0]
    947             dist = CalcDistance(pos, internalPos)
    948             if dist < shortestDistance:
    949                 shortestDistance = dist
    950                 ret = internalPos
    951 
    952         return ret
    953 
    954     @telemetry.ZONE_METHOD
    955     def UpdateFields(self, fieldName, field, secondaryFieldName, secField, delta):
    956         threshold = 0.0001
    957         markerPos = field['MarkerPosition']
    958         if not len(markerPos) == 3:
    959             print('Warning: Field ' + fieldName + ' passing in a 2D coordinate, not a 3D one: ' + str(markerPos))
    960             return {}
    961         moveScale = 0.03
    962         actualMarkerPos = [markerPos[0], markerPos[1], markerPos[2]]
    963         actualMarkerPos[0] += delta[0] * moveScale
    964         actualMarkerPos[2] += delta[1] * moveScale
    965         newMarkerPos = [actualMarkerPos[0], actualMarkerPos[1], actualMarkerPos[2]]
    966         newMarkerPos[0] += self.markerCorrection[0]
    967         newMarkerPos[2] += self.markerCorrection[1]
    968         tris = field['Tris']
    969         result = []
    970         for t in tris:
    971             points2D = [t[0][1], t[1][1], t[2][1]]
    972             a, b, c = self.CalcBaryCentric2DTriangle(points2D, (newMarkerPos[0], newMarkerPos[2]))
    973             if a >= 0 - threshold and b >= 0 - threshold and c >= 0 - threshold:
    974                 result = [[t[0][0], a], [t[1][0], b], [t[2][0], c]]
    975                 self.fieldLKG[fieldName] = result
    976                 self.fieldLKGPos[fieldName] = newMarkerPos
    977                 break
    978 
    979         if result != []:
    980             self.fieldResetData[fieldName] = newMarkerPos
    981         else:
    982             closestPos = self.CalculateClosestPosInField(newMarkerPos, field)
    983             for t in tris:
    984                 points2D = [t[0][1], t[1][1], t[2][1]]
    985                 a, b, c = self.CalcBaryCentric2DTriangle(points2D, closestPos)
    986                 if a >= 0 - threshold and b >= 0 - threshold and c >= 0 - threshold:
    987                     result = [[t[0][0], a], [t[1][0], b], [t[2][0], c]]
    988                     self.fieldLKG[fieldName] = result
    989                     self.fieldLKGPos[fieldName] = closestPos
    990                     break
    991 
    992             self.fieldResetData[fieldName] = [closestPos[0], 0.0, closestPos[1]]
    993             self.markerCorrection = [closestPos[0] - actualMarkerPos[0], closestPos[1] - actualMarkerPos[2]]
    994         finalAttrs = {}
    995         newAttrs = {}
    996         for r in result:
    997             if r[0] in field['VertData']:
    998                 attrs = field['VertData'][r[0]]
    999                 weight = r[1]
   1000                 for a in attrs:
   1001                     if a[0] in field['Attributes']:
   1002                         if a[0] not in newAttrs:
   1003                             newAttrs[a[0]] = weight * a[1]
   1004                         else:
   1005                             newAttrs[a[0]] += weight * a[1]
   1006 
   1007         for newAttr in newAttrs:
   1008             finalAttrs[newAttr] = newAttrs[newAttr]
   1009 
   1010         if secField:
   1011             secMarkerPos = list(secField['MarkerPosition'])
   1012             if len(secMarkerPos) == 3:
   1013                 secMarkerPos[2] = newMarkerPos[2]
   1014                 tris = secField['Tris']
   1015                 result = []
   1016                 for t in tris:
   1017                     points2D = [t[0][1], t[1][1], t[2][1]]
   1018                     a, b, c = self.CalcBaryCentric2DTriangle(points2D, (secMarkerPos[0], secMarkerPos[2]))
   1019                     if a >= 0 - threshold and b >= 0 - threshold and c >= 0 - threshold:
   1020                         result = [[t[0][0], a], [t[1][0], b], [t[2][0], c]]
   1021                         self.fieldLKG[secondaryFieldName] = result
   1022                         self.fieldLKGPos[secondaryFieldName] = secMarkerPos
   1023 
   1024                 if result != []:
   1025                     self.fieldResetData[secondaryFieldName] = secMarkerPos
   1026                 elif secondaryFieldName in self.fieldLKG:
   1027                     result = self.fieldLKG[secondaryFieldName]
   1028                     self.fieldResetData[secondaryFieldName] = self.fieldLKGPos[secondaryFieldName]
   1029                 else:
   1030                     print("Warning: Can't find secondary field in LKG!")
   1031                 secFinalAttrs = {}
   1032                 for r in result:
   1033                     if r[0] in secField['VertData']:
   1034                         attrs = secField['VertData'][r[0]]
   1035                         weight = r[1]
   1036                         for a in attrs:
   1037                             if a[0] in secField['Attributes']:
   1038                                 if a[0] not in secFinalAttrs:
   1039                                     secFinalAttrs[a[0]] = weight * a[1]
   1040                                 else:
   1041                                     secFinalAttrs[a[0]] += weight * a[1]
   1042 
   1043                 if secFinalAttrs != {}:
   1044                     for a in secFinalAttrs:
   1045                         if a in finalAttrs:
   1046                             finalAttrs[a] = (finalAttrs[a] + secFinalAttrs[a]) / 2.0
   1047                         else:
   1048                             finalAttrs[a] = secFinalAttrs[a]
   1049 
   1050                 else:
   1051                     print('Secondary out of range')
   1052             else:
   1053                 print('Warning: Secondary marker coordinate not 3 values.')
   1054         return finalAttrs
   1055 
   1056     @telemetry.ZONE_METHOD
   1057     def UpdateFieldAnimation(self, delta):
   1058         mirror = True
   1059         delta[0] = -delta[0]
   1060         if self.pickedMesh == 'head':
   1061             fieldName = self.mapping[self.zone]
   1062         else:
   1063             fieldName = 'Body'
   1064         animFieldName = ''
   1065         if fieldName == 'OuterBrow':
   1066             animFieldName = 'EyebrowOuter'
   1067         elif fieldName == 'InnerBrow':
   1068             animFieldName = 'EyebrowInner'
   1069         elif fieldName == 'Chin':
   1070             animFieldName = 'Jaw'
   1071             mirror = False
   1072         elif fieldName == 'MouthCorner':
   1073             animFieldName = 'MouthCorner'
   1074         elif fieldName in ('EyesInner', 'EyesOuter'):
   1075             animFieldName = 'EyeClose'
   1076         elif fieldName == 'Eyes':
   1077             animFieldName = 'EyeRotation'
   1078             mirror = False
   1079         elif fieldName == 'CheeksUpper':
   1080             animFieldName = 'CheeksUpper'
   1081         elif fieldName == 'UpperLip':
   1082             animFieldName = 'UpperLip'
   1083         elif fieldName == 'Jaw' or fieldName == 'JawCheek' or fieldName == 'Ears':
   1084             animFieldName = 'Forehead'
   1085             mirror = False
   1086         elif fieldName == 'Body':
   1087             animFieldName = 'Torso'
   1088             mirror = False
   1089         else:
   1090             animFieldName = 'HeadLookAt'
   1091             mirror = False
   1092         if mirror and self.pickedSide == 'right':
   1093             delta[0] = -delta[0]
   1094         if animFieldName in self.fieldDataAnimation['Fields']:
   1095             field = self.fieldDataAnimation['Fields'][animFieldName]
   1096             secField = None
   1097             attrs = self.UpdateFields(animFieldName, field, None, secField, delta)
   1098             return attrs
   1099         else:
   1100             return
   1101 
   1102     @telemetry.ZONE_METHOD
   1103     def UpdateField(self, delta):
   1104         if self.zone not in self.mapping:
   1105             return {}
   1106         if self.pickedMesh == 'head':
   1107             fieldName = self.mapping[self.zone]
   1108         else:
   1109             fieldName = 'Body'
   1110             if self.zone in self.bodyMapping:
   1111                 fieldName = self.bodyMapping[self.zone]
   1112         secondaryFieldName = ''
   1113         if self.direction == 'left' or self.direction == 'right':
   1114             secondaryFieldName = fieldName + '_Front'
   1115             fieldName = fieldName + '_Side'
   1116             if self.direction == 'left':
   1117                 delta[0] = -delta[0]
   1118         else:
   1119             secondaryFieldName = fieldName + '_Side'
   1120             fieldName += '_Front'
   1121             if self.pickedSide == 'left':
   1122                 delta[0] = -delta[0]
   1123         if fieldName in self.fieldData['Fields']:
   1124             field = self.fieldData['Fields'][fieldName]
   1125             secField = None
   1126             if secondaryFieldName in self.fieldData['Fields']:
   1127                 secField = self.fieldData['Fields'][secondaryFieldName]
   1128             attrs = self.UpdateFields(fieldName, field, secondaryFieldName, secField, delta)
   1129             return attrs
   1130         return {}
   1131 
   1132     @telemetry.ZONE_METHOD
   1133     def UpdateFieldsBasedOnExistingValues(self, doll):
   1134         if not doll:
   1135             return
   1136         mods = doll.buildDataManager.GetModifiersAsList()
   1137         fieldGroups = {}
   1138         for m in mods:
   1139             if len(m.name.split('_')) > 1:
   1140                 if m.categorie == 'facemodifiers' or m.categorie == 'bodyshapes':
   1141                     group = m.name.split('_')[0]
   1142                     if group not in fieldGroups:
   1143                         fieldGroups[group] = {}
   1144                     axisName = m.name.split('_')[1][:-5]
   1145                     fieldGroups[group][axisName] = m.weight
   1146 
   1147         for f in self.fieldData['Fields']:
   1148             type = f.split('_')[0].lower()
   1149             direction = f.split('_')[1].lower()
   1150             field = self.fieldData['Fields'][f]
   1151             if type in fieldGroups:
   1152                 attrs = fieldGroups[type]
   1153                 x = 0.0
   1154                 y = 0.0
   1155                 for a in attrs:
   1156                     if a == 'up':
   1157                         y = attrs[a] * 2
   1158                     elif a == 'down':
   1159                         y = -attrs[a] * 2
   1160                     elif a == 'left' and direction == 'front':
   1161                         x = -attrs[a] * 2
   1162                     elif a == 'right' and direction == 'front':
   1163                         x = attrs[a] * 2
   1164                     elif a == 'forward' and direction == 'side':
   1165                         x = attrs[a] * 2
   1166                     elif a == 'back' and direction == 'side':
   1167                         x = -attrs[a] * 2
   1168 
   1169                 newPos = [x, 0.0, y]
   1170                 field['MarkerPosition'] = newPos
   1171                 self.fieldResetData[f] = newPos
   1172             else:
   1173                 newPos = [0, 0, 0]
   1174                 field['MarkerPosition'] = newPos
   1175                 self.fieldResetData[f] = newPos
   1176 
   1177         for meshPicked in ['head', 'body']:
   1178             self.pickedMesh = meshPicked
   1179             for z in range(20):
   1180                 self.zone = z
   1181                 self.UpdateField([0, 0])
   1182 
   1183         self.zone = 0
   1184         result = self.UpdateField([0, 0])