def getValueAsString(self, i:int = 0)->str: value = self.getValue(i) if value == None: return '' # convert floats to strings if isinstance(value, float): value = floatToStr(value) # convert ints to strings elif isinstance(value, int): value = str(value) # convert lists to strings elif isinstance(value, list) or isinstance(value, tuple) and len(value) > 0: _value = [] for i in range(0, len(value)): # convert floats to strings if isinstance(value[i], float): _value.append(floatToStr(value[i])) else: _value.append(str(value[i])) value = '\t'.join(_value) elif not isinstance(value, str): value = '' return value
def getValueAsString(self, i: int = 0) -> str: value = self.getValue(i) if value == None: return '' # convert floats to strings if isinstance(value, float): value = floatToStr(value) # convert ints to strings elif isinstance(value, int): value = str(value) # convert lists to strings elif isinstance(value, list) or isinstance(value, tuple) and len(value) > 0: _value = [] for i in range(0, len(value)): # convert floats to strings if isinstance(value[i], float): _value.append(floatToStr(value[i])) else: _value.append(str(value[i])) value = '\t'.join(_value) elif not isinstance(value, str): value = '' return value
def getValueAsString(self, i: int = 0) -> str: ''' Gets the value of value[i] as a formatted string. If self.value[i] is a list, the list's string representation will be seperated with '\t's instead of ','s ''' value = self.getValue(i) if value is None: return '' # convert floats to strings if isinstance(value, float): value = floatToStr(value) # convert ints to strings elif isinstance(value, int): value = str(value) # convert lists to strings elif isinstance(value, list) or isinstance(value, tuple) and len(value) > 0: value = tuple(value) # satisfies pylint "value is unsubscriptable" _value = [] for i in range(0, len(value)): # convert floats to strings if isinstance(value[i], float): _value.append(floatToStr(value[i])) else: _value.append(str(value[i])) value = '\t'.join(_value) elif not isinstance(value, str): value = '' return value
def parseAttributeValue(self, value, blenderObject): if str(value).find('{{xyz}}') != -1: return str(value).replace('{{xyz}}', '%6.8f\t%6.8f\t%6.8f' % ( xplane_helpers.floatToStr(blenderObject.location[0]), xplane_helpers.floatToStr(blenderObject.location[2]), xplane_helpers.floatToStr(-blenderObject.location[1]) )) return value
def parseAttributeValue(self, value, blenderObject): if str(value).find('{{xyz}}') != -1: return str(value).replace( '{{xyz}}', '%6.8f\t%6.8f\t%6.8f' % (xplane_helpers.floatToStr(blenderObject.location[0]), xplane_helpers.floatToStr(blenderObject.location[2]), xplane_helpers.floatToStr(-blenderObject.location[1]))) return value
def _writeStaticRotation(self, bakeMatrix): debug = getDebug() indent = self.getIndent() o = '' bakeMatrix = bakeMatrix rotation = bakeMatrix.to_euler('XYZ') rotation[0] = round(rotation[0],5) rotation[1] = round(rotation[1],5) rotation[2] = round(rotation[2],5) # ignore noop rotations if rotation[0] == 0 and rotation[1] == 0 and rotation[2] == 0: return o if debug: o += indent + '# static rotation\n' # Ben says: this is SLIGHTLY counter-intuitive...Blender axes are # globally applied in a Euler, so in our XYZ, X is affected -by- Y # and both are affected by Z. # # Since X-Plane works opposite this, we are going to apply the # animations exactly BACKWARD! ZYX. The order here must # be opposite the decomposition order above. # # Note that since our axis naming is ALSO different this will # appear in the OBJ file as Y -Z X. # # see also: http://hacksoflife.blogspot.com/2015/11/blender-notepad-eulers.html axes = (2, 1, 0) eulerAxes = [(0.0,0.0,1.0),(0.0,1.0,0.0),(1.0,0.0,0.0)] i = 0 for axis in eulerAxes: deg = math.degrees(rotation[axes[i]]) # ignore zero rotation if not deg == 0: o += indent + 'ANIM_rotate\t%s\t%s\t%s\t%s\t%s\n' % ( floatToStr(axis[0]), floatToStr(axis[2]), floatToStr(-axis[1]), floatToStr(deg), floatToStr(deg) ) i += 1 return o
def _writeEulerRotationKeyframes(self, dataref, keyframes): debug = getDebug() o = '' indent = self.getIndent() axes, final_rotation_mode = keyframes.getReferenceAxes() totalRot = 0 for axis,order in zip(axes,XPlaneKeyframeCollection.EULER_AXIS_ORDERING[final_rotation_mode]): ao = '' totalAxisRot = 0 ao += "%sANIM_rotate_begin\t%s\t%s\t%s\t%s\n" % ( indent, floatToStr(axis[0]), floatToStr(axis[2]), floatToStr(-axis[1]), dataref ) for keyframe in keyframes: deg = math.degrees(keyframe.rotation[order]) totalRot += abs(deg) totalAxisRot += abs(deg) ao += "%sANIM_rotate_key\t%s\t%s\n" % ( indent, floatToStr(keyframe.value), floatToStr(deg) ) ao += self._writeKeyframesLoop(dataref) ao += "%sANIM_rotate_end\n" % indent # do not write non-animated axis if round(totalAxisRot, FLOAT_PRECISION) > 0: o += ao # do not write zero rotations if round(totalRot, FLOAT_PRECISION) == 0: return '' return o
def _writeAxisAngleRotationKeyframes(self, dataref, keyframes): o = '' indent = self.getIndent() totalRot = 0 # our reference axis (or axes) axes, final_rotation_mode = keyframes.getReferenceAxes() if len(axes) == 3: # decompose to eulers and return euler rotation instead o = self._writeEulerRotationKeyframes(dataref,keyframes.asEuler()) return o elif len(axes) == 1: refAxis = axes[0] o += "%sANIM_rotate_begin\t%s\t%s\t%s\t%s\n" % ( indent, floatToStr(refAxis[0]), floatToStr(refAxis[2]), floatToStr(-refAxis[1]), dataref ) for keyframe in keyframes: deg = math.degrees(keyframe.rotation[0]) totalRot += abs(deg) o += "%sANIM_rotate_key\t%s\t%s\n" % ( indent, floatToStr(keyframe.value), floatToStr(deg) ) o += self._writeKeyframesLoop(dataref) o += "%sANIM_rotate_end\n" % indent # do not write zero rotations if round(totalRot, FLOAT_PRECISION) == 0: return '' return o
def _writeStaticTranslation(self, bakeMatrix): debug = getDebug() indent = self.getIndent() o = '' bakeMatrix = bakeMatrix translation = bakeMatrix.to_translation() translation[0] = round(translation[0],5) translation[1] = round(translation[1],5) translation[2] = round(translation[2],5) # ignore noop translations if translation[0] == 0 and translation[1] == 0 and translation[2] == 0: return o if debug: o += indent + '# static translation\n' o += indent + 'ANIM_trans\t%s\t%s\t%s\t%s\t%s\t%s\n' % ( floatToStr(translation[0]), floatToStr(translation[2]), floatToStr(-translation[1]), floatToStr(translation[0]), floatToStr(translation[2]), floatToStr(-translation[1]) ) return o
def _writeTranslationKeyframes(self, dataref): debug = getDebug() keyframes = self.animations[dataref] o = '' if not self.isDataRefAnimatedForTranslation(): return o # Apply scaling to translations pre_loc, pre_rot, pre_scale = self.getPreAnimationMatrix().decompose() totalTrans = 0 indent = self.getIndent() if debug: o += indent + '# translation keyframes\n' o += "%sANIM_trans_begin\t%s\n" % (indent, dataref) for keyframe in keyframes: totalTrans += abs(keyframe.location[0]) + abs(keyframe.location[1]) + abs(keyframe.location[2]) o += "%sANIM_trans_key\t%s\t%s\t%s\t%s\n" % ( indent, floatToStr(keyframe.value), floatToStr(keyframe.location[0] * pre_scale[0]), floatToStr(keyframe.location[2] * pre_scale[2]), floatToStr(-keyframe.location[1] * pre_scale[1]) ) o += self._writeKeyframesLoop(dataref) o += "%sANIM_trans_end\n" % indent # do not write zero translations if totalTrans == 0: return '' return o
def write(self) -> str: """ Writes the combined Blender and XPlane2Blender data, raises UnwritableXPlaneType if logger errors found """ debug = xplane_config.getDebug() indent = self.xplaneBone.getIndent() o = super().write() special_empty_props = self.blenderObject.xplane.special_empty_props if (int(bpy.context.scene.xplane.version) >= 1130 and (special_empty_props.special_type == EMPTY_USAGE_EMITTER_PARTICLE or special_empty_props.special_type == EMPTY_USAGE_EMITTER_SOUND)): if not self.xplaneBone.xplaneFile.options.particle_system_file.endswith( ".pss"): logger.error( "Particle emitter {} is used, despite no .pss file being set" .format(self.blenderObject.name)) return "" elif special_empty_props.emitter_props.name.strip() == "": logger.error( "Particle name for emitter {} can't be blank".format( self.blenderObject.name)) return "" bake_matrix = self.xplaneBone.getBakeMatrixForAttached() em_location = xplane_helpers.vec_b_to_x( bake_matrix.to_translation()) #yaw,pitch,roll theta, psi, phi = tuple( map(math.degrees, bake_matrix.to_euler()[:])) o += "{indent}EMITTER {name} {x} {y} {z} {phi} {theta} {psi}".format( indent=indent, name=special_empty_props.emitter_props.name, x=floatToStr(em_location.x), y=floatToStr(em_location.y), z=floatToStr(em_location.z), phi=floatToStr(-phi), #yaw right theta=floatToStr(theta), #pitch up psi=floatToStr(psi)) #roll right if (special_empty_props.emitter_props.index_enabled and special_empty_props.emitter_props.index >= 0): o += ' {}'.format(special_empty_props.emitter_props.index) o += '\n' elif (int(bpy.context.scene.xplane.version) >= 1130 and special_empty_props.special_type == EMPTY_USAGE_MAGNET): bake_matrix = self.xplaneBone.getBakeMatrixForAttached() em_location = xplane_helpers.vec_b_to_x( bake_matrix.to_translation()) #yaw,pitch,roll theta, psi, phi = tuple( map(math.degrees, bake_matrix.to_euler()[:])) o += "{indent}MAGNET {debug_name} {magnet_type} {x} {y} {z} {phi} {theta} {psi}\n".format( indent=indent, debug_name=special_empty_props.magnet_props.debug_name, magnet_type=self.magnet_type, x=floatToStr(em_location.x), y=floatToStr(em_location.y), z=floatToStr(em_location.z), phi=floatToStr(-phi), #yaw right theta=floatToStr(theta), #pitch up psi=floatToStr(psi)) #roll right return o