charCreationCamera.py (14466B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\camera\charCreationCamera.py 2 import math 3 import geo2 4 import blue 5 import mathext 6 import signals 7 import charactercreator.const as ccConst 8 from carbonui.camera.behaviors.cameraBehavior import CameraBehavior 9 from carbonui.camera.polarCamera import PolarCamera 10 from carbonui.util.various_unsorted import GetAttrs 11 CONTROL_NONE = 0 12 CONTROL_VERTICAL = 1 13 CONTROL_BOTH = 2 14 15 class CharCreationCamera(PolarCamera): 16 17 def __init__(self, avatar, modeName = ccConst.CAMERA_MODE_DEFAULT): 18 PolarCamera.__init__(self) 19 self.cameraModeChanged = signals.Signal(signalName='cameraModeChanged') 20 self.frontClip = 0.1 21 self.backClip = 1000.0 22 self.pitch = math.pi / 2 23 self._transformDistance = None 24 self._transformPOI = None 25 self._rotateAvatar = None 26 self._onFocusValue = False 27 self.moveCallback = None 28 self.updateFocus = True 29 self.AddBehavior(CharCreationCameraHandler()) 30 self.focus = (0.0, 0.0, 0.0) 31 self.xFactor = 0.0 32 self.yFactor = 0.0 33 self._currentMode = None 34 self.ToggleMode(modeName, avatar) 35 36 def ToggleMode(self, modeName, avatar = None, transformTime = None): 37 self.avatar = avatar 38 self.UpdateAvatarStats() 39 self.updateFocus = True 40 self.xTarget = 0.0 41 self.yTarget = 0.0 42 setup = {'controlStyle': CONTROL_NONE, 43 'farDistance': 1000.0, 44 'nearDistance': 1.0, 45 'minRotation': None, 46 'maxRotation': None, 47 'fieldOfView': 0.9, 48 'minPanX': -0.14, 49 'maxPanX': 0.12, 50 'minPanY': -0.12, 51 'maxPanY': 0.12, 52 'nearPan': 0.0, 53 'nearFocusY': 0.0, 54 'farFocusY': 0.0, 55 'nearMinTilt': 0.0, 56 'nearMaxTilt': math.pi, 57 'farMinTilt': 0.0, 58 'farMaxTilt': math.pi, 59 'distance': 1.7, 60 'poi': (0, 1.5, 0), 61 'rotateAvatar': True} 62 if modeName in (ccConst.CAMERA_MODE_FACE, ccConst.CAMERA_MODE_BODY): 63 setup['controlStyle'] = CONTROL_VERTICAL 64 setup['fieldOfView'] = 0.3 65 setup['farDistance'] = 8.0 66 setup['nearDistance'] = 0.9 67 setup['minPanX'] = 0.0 68 setup['maxPanX'] = 0.0 69 setup['minPanY'] = -1.5 70 setup['maxPanY'] = 0.1 71 setup['nearPan'] = 0.0 72 setup['nearFocusY'] = 0.0 73 setup['farFocusY'] = -self.avatarEyeHeight / 2.0 + 0.1 74 setup['nearMinTilt'] = math.pi * 0.4 75 setup['nearMaxTilt'] = math.pi * 0.66 76 setup['farMinTilt'] = math.pi * 0.5 77 setup['farMaxTilt'] = math.pi * 0.5 78 if modeName == ccConst.CAMERA_MODE_FACE: 79 setup['distance'] = setup['nearDistance'] + 0.5 80 setup['poi'] = (0, -setup['nearFocusY'], 0) 81 setup['yFactor'] = setup['nearFocusY'] 82 else: 83 setup['distance'] = setup['farDistance'] 84 setup['poi'] = (0, -setup['farFocusY'], 0) 85 setup['yFactor'] = setup['farFocusY'] 86 elif modeName == ccConst.CAMERA_MODE_PORTRAIT: 87 setup['controlStyle'] = CONTROL_BOTH 88 setup['minRotation'] = 1.0 89 setup['maxRotation'] = 2.5 90 setup['fieldOfView'] = 0.3 91 setup['farDistance'] = 2.5 92 setup['nearDistance'] = 0.6 93 setup['minPanX'] = -0.2 94 setup['maxPanX'] = 0.2 95 setup['minPanY'] = -0.15 96 setup['maxPanY'] = 0.15 97 setup['nearPan'] = 0.07 98 setup['nearFocusY'] = 0.0 99 setup['farFocusY'] = 0.0 100 setup['nearMinTilt'] = math.pi * 0.33 101 setup['nearMaxTilt'] = math.pi * 0.66 102 setup['farMinTilt'] = math.pi * 0.33 103 setup['farMaxTilt'] = math.pi * 0.66 104 setup['distance'] = setup['nearDistance'] + 0.5 105 setup['rotateAvatar'] = False 106 self._transformDistance = None 107 self._transformPOI = None 108 if (self._rotateAvatar is None or self._rotateAvatar != setup['rotateAvatar']) and self.avatar: 109 self.avatar.rotation = (0, 0, 0, 1) 110 self.yaw = math.pi / 2 111 self._rotateAvatar = setup['rotateAvatar'] 112 now = blue.os.GetWallclockTime() 113 for k, v in setup.iteritems(): 114 if transformTime: 115 if k == 'distance': 116 self._transformDistance = (now, 117 self.distance, 118 v, 119 transformTime) 120 elif k == 'poi': 121 self._transformPOI = (now, 122 (0.0, -self.yFactor, 0.0), 123 v, 124 transformTime) 125 self._onFocusValue = True 126 else: 127 setattr(self, k, v) 128 else: 129 setattr(self, k, v) 130 131 self._ChangeMode(modeName) 132 133 def _ChangeMode(self, newMode): 134 previousMode = self._currentMode 135 self._currentMode = newMode 136 if previousMode != self._currentMode: 137 self.cameraModeChanged(previousMode, self._currentMode) 138 139 def UpdatePortraitInfo(self): 140 self.UpdateAvatarStats() 141 self.nearFocusY = self.avatarEyeHeight 142 self.farFocusY = self.avatarEyeHeight 143 144 def UpdateAvatar(self, avatar): 145 self.avatar = avatar 146 147 def GetBonePosition(self, boneName): 148 if self.avatar is not None and self.avatar.animationUpdater is not None: 149 joint = self.avatar.GetBoneIndex(boneName) 150 if joint == 4294967295L: 151 return (0.0, 0.0, 0.0) 152 ret = self.avatar.GetBonePosition(joint) 153 return ret 154 return (0.0, 0.0, 0.0) 155 156 def UpdateAvatarStats(self): 157 if self.avatar is not None and self.avatar.animationUpdater is not None: 158 blue.resMan.Wait() 159 pos = self.GetBonePosition('fj_eyeballRight') 160 self.avatarEyeHeight = pos[1] 161 else: 162 self.avatarEyeHeight = 0.0 163 164 def PlacePortraitCamera(self, pos, poi): 165 direction = geo2.Subtract(pos, poi) 166 self.distance = geo2.Vec3Length(direction) 167 direction = geo2.Vec3Normalize(direction) 168 self.yaw = math.acos(direction[0]) 169 self.pitch = math.asin(direction[1]) + math.pi / 2.0 170 right = self.GetBonePosition('fj_eyeballRight') 171 left = self.GetBonePosition('fj_eyeballLeft') 172 self.focus = geo2.Add(right, left) 173 self.focus = geo2.Vector(*self.focus) * 0.5 174 xFactor, yFactor = self.GetCorrectCameraXandYFactors(pos, poi) 175 self.xFactor = xFactor 176 self.yFactor = yFactor 177 178 def GetCorrectCameraXandYFactors(self, position, poi): 179 cameraZ = geo2.Vec3Normalize(geo2.Subtract(position, poi)) 180 cameraX = geo2.Vec3Cross(cameraZ, (0.0, 1.0, 0.0)) 181 cameraY = geo2.Vec3Cross(cameraZ, cameraX) 182 cameraMatrix = ((cameraX[0], 183 cameraY[0], 184 cameraZ[0], 185 0.0), 186 (cameraX[1], 187 cameraY[1], 188 cameraZ[1], 189 0.0), 190 (cameraX[2], 191 cameraY[2], 192 cameraZ[2], 193 0.0), 194 (0.0, 0.0, 0.0, 1.0)) 195 offset = geo2.Subtract(self.focus, poi) 196 res = geo2.Vec3Transform(offset, cameraMatrix) 197 return (res[0], res[1]) 198 199 def Zoom(self, delta): 200 newFov = self.fieldOfView + delta * 0.0005 201 newFov = min(self.maxFov, max(self.minFov, newFov)) 202 self.fieldOfView = newFov 203 self.xFactor = self.xTarget 204 self.yFactor = self.yTarget 205 206 def Dolly(self, delta): 207 if self.controlStyle == CONTROL_NONE: 208 return 209 if self.controlStyle == CONTROL_BOTH: 210 self.xFactor = self.xTarget 211 self.yFactor = self.yTarget 212 newDistance = min(self.farDistance, max(self.nearDistance, self.distance - self.distance * (delta * 0.0025))) 213 self.distance = newDistance 214 if self._currentMode == ccConst.CAMERA_MODE_FACE and newDistance > 0.75 * self.farDistance: 215 self._ChangeMode(ccConst.CAMERA_MODE_BODY) 216 elif self._currentMode == ccConst.CAMERA_MODE_BODY and newDistance < self.nearDistance + 0.5: 217 self._ChangeMode(ccConst.CAMERA_MODE_FACE) 218 if self._onFocusValue: 219 portion = self.GetPortionFromDistance() 220 self.yFactor = mathext.lerp(self.nearFocusY, self.farFocusY, portion) 221 222 def GetPortionFromDistance(self): 223 minmaxRange = self.farDistance - self.nearDistance 224 portion = (self.distance - self.nearDistance) / minmaxRange 225 return portion 226 227 def GetPortionFromFieldOfView(self): 228 minmaxRange = self.maxFov - self.minFov 229 portion = (self.fieldOfView - self.minFov) / minmaxRange 230 return portion 231 232 def GetFocusYBasedOnDistance(self): 233 portion = self.GetPortionFromDistance() 234 focusRange = self.farFocusY - self.nearFocusY 235 return self.nearFocusY + focusRange * portion 236 237 def GetMinMaxPitchBasedOnDistance(self): 238 portion = self.GetPortionFromDistance() 239 minRange = self.farMinTilt - self.nearMinTilt 240 maxRange = self.farMaxTilt - self.nearMaxTilt 241 return (self.nearMinTilt + minRange * portion, self.nearMaxTilt + maxRange * portion) 242 243 def Pan(self, dx, dy): 244 if self.controlStyle == CONTROL_NONE: 245 return 246 self.xFactor -= dx * 0.0005 247 self.yFactor += dy * 0.0005 * self.distance 248 self.xTarget = self.xFactor 249 self.yTarget = self.yFactor 250 self._onFocusValue = False 251 252 def GetPanLimitsBasedOnDistance(self): 253 if self.controlStyle == CONTROL_VERTICAL: 254 portion = 1.0 255 else: 256 portion = self.GetPortionFromDistance() 257 return ((self.minPanX * portion - self.nearPan, self.maxPanX * portion + self.nearPan), (self.minPanY * portion - self.nearPan, self.maxPanY * portion + self.nearPan)) 258 259 def LimitPanning(self): 260 xLimits, yLimits = self.GetPanLimitsBasedOnDistance() 261 if self.controlStyle == CONTROL_BOTH: 262 xAxis = geo2.Vector(self.viewMatrix.transform[0][0], self.viewMatrix.transform[1][0], self.viewMatrix.transform[2][0]) 263 yAxis = geo2.Vector(self.viewMatrix.transform[0][1], self.viewMatrix.transform[1][1], self.viewMatrix.transform[2][1]) 264 else: 265 xAxis = geo2.Vector(1.0, 0.0, 0.0) 266 yAxis = geo2.Vector(0.0, 1.0, 0.0) 267 self.xFactor = min(xLimits[1], max(xLimits[0], self.xFactor)) 268 self.yFactor = min(yLimits[1], max(yLimits[0], self.yFactor)) 269 horizontal = xAxis * self.xFactor 270 vertical = yAxis * self.yFactor 271 offset = horizontal + vertical 272 rotatedFocus = geo2.QuaternionTransformVector(self.avatar.rotation, self.focus) 273 newPoi = rotatedFocus + offset 274 self.SetPointOfInterest(newPoi) 275 276 def AdjustYaw(self, delta): 277 if self.controlStyle == CONTROL_NONE: 278 return 279 if self._rotateAvatar: 280 delta = delta * 0.005 281 yaw, pitch, roll = geo2.QuaternionRotationGetYawPitchRoll(self.avatar.rotation) 282 yaw = yaw + delta 283 if self.maxRotation is not None and self.minRotation is not None: 284 if yaw < math.pi: 285 yaw = min(yaw, self.minRotation) 286 else: 287 yaw = max(yaw, self.maxRotation) 288 rotation = geo2.QuaternionRotationSetYawPitchRoll(yaw, pitch, roll) 289 self.avatar.rotation = rotation 290 charSvc = sm.GetService('character') 291 if GetAttrs(charSvc, 'sculpting', 'highlightGhost', 'avatar'): 292 charSvc.sculpting.highlightGhost.avatar.rotation = rotation 293 if GetAttrs(charSvc, 'sculpting', 'bodyHighlightGhost', 'avatar'): 294 charSvc.sculpting.bodyHighlightGhost.avatar.rotation = rotation 295 self.Update() 296 else: 297 PolarCamera.AdjustYaw(self, delta * 0.005) 298 299 def SetYaw(self, yaw, ignoreUpdate = True): 300 if self.maxRotation is not None and self.minRotation is not None: 301 yaw = max(self.minRotation, min(self.maxRotation, yaw)) 302 PolarCamera.SetYaw(self, yaw) 303 304 def AdjustPitch(self, delta): 305 if self.controlStyle == CONTROL_NONE: 306 return 307 self.minPitch, self.maxPitch = self.GetMinMaxPitchBasedOnDistance() 308 PolarCamera.AdjustPitch(self, delta * 0.005) 309 310 def Update(self): 311 if self.updateFocus: 312 right = self.GetBonePosition('fj_eyeballRight') 313 left = self.GetBonePosition('fj_eyeballLeft') 314 self.focus = geo2.Add(right, left) 315 self.focus = geo2.Vector(*self.focus) * 0.5 316 if self.moveCallback: 317 self.moveCallback(self.viewMatrix) 318 if self.controlStyle == CONTROL_VERTICAL: 319 length = geo2.Vec2Length(geo2.Vector(self.focus[0], self.focus[2])) 320 self.focus = (0.0, self.focus[1], length) 321 self.updateFocus = False 322 PolarCamera.Update(self) 323 324 def SetMoveCallback(self, cb): 325 self.moveCallback = cb 326 327 328 class CharCreationCameraHandler(CameraBehavior): 329 330 def __init__(self): 331 CameraBehavior.__init__(self) 332 333 @staticmethod 334 def GetName(self): 335 return 'CharCreationCameraHandler' 336 337 def ProcessCameraUpdate(self, camera, now, frameTime): 338 if camera._transformDistance: 339 startTime, startValue, toValue, duration = camera._transformDistance 340 ndt = min(1.0, blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) / duration) 341 camera.distance = mathext.lerp(startValue, toValue, ndt) 342 if ndt >= 1.0: 343 camera._transformDistance = None 344 if camera._transformPOI: 345 startTime, startValue, toValue, duration = camera._transformPOI 346 ndt = min(1.0, blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) / duration) 347 camera.yFactor = mathext.lerp(-startValue[1], -toValue[1], ndt) 348 if ndt >= 1.0: 349 camera._transformPOI = None 350 if camera.controlStyle != CONTROL_NONE: 351 camera.minPitch, camera.maxPitch = camera.GetMinMaxPitchBasedOnDistance() 352 camera.pitch = min(camera.maxPitch, max(camera.minPitch, camera.pitch)) 353 camera.LimitPanning() 354 camera.UpdateProjectionMatrix()