openVRengine.py
You can view and download this file on Github: openVRengine.py
1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2# This is an EXUDYN example
3#
4# Details: test creating piston engine with variable number of pistons and piston angles;
5# possibility to interact with openVR
6#
7# Author: Johannes Gerstmayr
8# Date: 2023-01-17
9#
10# Copyright:This file is part of Exudyn. Exudyn is free software. You can redistribute it and/or modify it under the terms of the Exudyn license. See 'LICENSE.txt' for more details.
11#
12#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13
14
15import exudyn as exu
16from exudyn.utilities import *
17from math import sin, cos, asin, acos, pi, exp, log, tan, atan, radians
18from exudyn.interactive import InteractiveDialog
19
20
21omegaDrive = 4*pi*0.5
22tEnd = 3600
23nodeType = exu.NodeType.RotationEulerParameters
24fixedSpeed = False #if false, the speed is given only for first 1 second
25
26# nodeType = exu.NodeType.RotationRxyz
27#nodeType = exu.NodeType.RotationRotationVector
28
29# import matplotlib.pyplot as plt
30# plt.close('all')
31zOffAdd = -0.5
32
33class EngineParameters:
34 def __init__(self, crankAnglesDegrees=[], pistonAnglesDegrees=[]):
35 #parameters in m, s, kg, rad, ...
36 self.crankAnglesDegrees = crankAnglesDegrees
37 if pistonAnglesDegrees == []:
38 self.pistonAnglesDegrees = list(0*np.array(crankAnglesDegrees))
39 else:
40 self.pistonAnglesDegrees = pistonAnglesDegrees
41
42 crankAngles = pi/180*np.array(crankAnglesDegrees)
43 self.crankAngles = list(crankAngles)
44
45 pistonAngles = pi/180*np.array(self.pistonAnglesDegrees)
46 self.pistonAngles = list(pistonAngles)
47
48 densitySteel = 7850
49 #kinematics & inertia & drawing
50 fZ = 1#0.2
51 self.pistonDistance = 0.08
52 self.pistonMass = 0.5
53 self.pistonLength = 0.05
54 self.pistonRadius = 0.02
55
56 self.conrodLength = 0.1 #X
57 self.conrodHeight = 0.02*fZ#Y
58 self.conrodWidth = 0.02*fZ #Z
59 self.conrodRadius = 0.012*fZ #Z
60
61 self.crankArmLength = 0.04 #X
62 self.crankArmHeight = 0.016 #Y
63 self.crankArmWidth = 0.01*fZ #Z width of arm
64 self.crankBearingWidth = 0.012*fZ #Z
65 self.crankBearingRadius = 0.01
66
67 self.conrodCrankCylLength = 0.024*fZ #Z; length of cylinder (bearing conrod-crank)
68 self.conrodCrankCylRadius = 0.008 #radius of cylinder (bearing conrod-crank)
69
70 self.pistonDistance = self.crankBearingWidth + 2*self.crankArmWidth + self.conrodCrankCylLength #Z distance
71
72 self.inertiaConrod = InertiaCuboid(densitySteel, sideLengths=[self.conrodLength, self.conrodHeight, self.conrodWidth])
73
74 eL = self.Length()
75 #last bearing:
76 densitySteel2 = densitySteel
77 self.inertiaCrank = InertiaCylinder(densitySteel2, self.crankBearingWidth, self.crankBearingRadius, axis=2).Translated([0,0,0.5*eL-0.5*self.crankBearingWidth])
78
79
80
81 for cnt, angle in enumerate(self.crankAngles):
82 A = RotationMatrixZ(angle)
83 zOff = -0.5*eL + cnt*self.pistonDistance
84 arm = InertiaCuboid(densitySteel2, sideLengths=[self.crankArmLength, self.crankArmHeight, self.crankArmWidth])
85 cylCrank = InertiaCylinder(densitySteel2, self.crankBearingWidth, self.crankBearingRadius, axis=2)
86 cylConrod = InertiaCylinder(densitySteel2, self.conrodCrankCylLength, self.conrodCrankCylRadius, axis=2)
87 #add inertias:
88 self.inertiaCrank += cylCrank.Translated([0,0,zOff+self.crankBearingWidth*0.5])
89 self.inertiaCrank += arm.Rotated(A).Translated(A@[self.crankArmLength*0.5,0,zOff+self.crankBearingWidth+self.crankArmWidth*0.5])
90 self.inertiaCrank += cylConrod.Translated(A@[self.crankArmLength,0,zOff+self.crankBearingWidth+self.crankArmWidth+self.conrodCrankCylLength*0.5])
91 self.inertiaCrank += arm.Rotated(A).Translated(A@[self.crankArmLength*0.5,0,zOff+self.crankBearingWidth+self.crankArmWidth*1.5+self.conrodCrankCylLength])
92
93 # self.inertiaCrank = InertiaCylinder(1e-8*densitySteel, length=self.pistonLength,
94 # outerRadius=self.pistonRadius, innerRadius=0.5*self.pistonRadius, axis=2)
95
96 self.inertiaPiston = InertiaCylinder(densitySteel, length=self.pistonLength,
97 outerRadius=self.pistonRadius, innerRadius=0.5*self.pistonRadius, axis=0)
98
99 #self.inertiaCrank.com = [0,0,0]
100 # print('crank COM=',np.array(self.inertiaCrank.com).round(8))
101 # print('inertiaCrank=',self.inertiaCrank)
102 # print('inertiaConrod=',self.inertiaConrod)
103 # print('inertiaPiston=',self.inertiaPiston)
104
105 def Length(self):
106 return self.pistonDistance*len(self.crankAngles) + self.crankBearingWidth
107
108 def MaxDimX(self):
109 return self.crankArmLength + self.conrodLength + self.pistonLength
110
111def ComputeSliderCrank(angleCrank, anglePiston, l1, l2):
112 phi1 = angleCrank-anglePiston
113 h = l1*sin(phi1) #height of crank-conrod bearing
114 phi2 = asin(h/l2) #angle of conrod in 2D slider-crank, corotated with piston rotation
115 angleConrod = anglePiston-phi2
116 Acr = RotationMatrixZ(angleConrod)
117 dp = l1*cos(phi1) + l2*cos(phi2) #distance of piston from crank rotation axis
118 return [phi1,phi2, angleConrod, Acr, dp]
119
120
121#this function (re-)creates gear geometry
122def CreateEngine(P):
123
124 colorCrank = color4grey
125 colorConrod = color4dodgerblue
126 colorPiston = color4brown[0:3]+[0.5]
127 showJoints = True
128
129 gravity = [0,-9.81*0,0]
130 eL = P.Length()
131 oGround=mbs.AddObject(ObjectGround(referencePosition= [0,0,zOffAdd], visualization=VObjectGround(graphicsData= [])))
132 nGround=mbs.AddNode(NodePointGround(referenceCoordinates = [0,0,zOffAdd]))
133
134 gEngine = [GraphicsDataOrthoCubePoint(centerPoint=[0,0,0], size=[P.MaxDimX()*2, P.MaxDimX(), eL*1.2],
135 color=[0.6,0.6,0.6,0.1], addEdges=True,
136 edgeColor = [0.8,0.8,0.8,0.3], addFaces=False)]
137 gEngine = [] #no block
138 #oEngine=mbs.AddObject(ObjectGround(referencePosition= [0,0,0], visualization=VObjectGround(graphicsData= gEngine)))
139 [nEngine, oEngine] = AddRigidBody(mbs, InertiaCuboid(1000, sideLengths=[1,1,1]), #dummy engine inertia
140 nodeType = nodeType,
141 position=[0,0,zOffAdd],
142 graphicsDataList = gEngine
143 )
144
145 mGround = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oGround))
146 mEngine = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oEngine))
147 sEngineForce = 0
148 oEngineJoint = 0
149 sEngineTorque = 0
150 oEngineJoint = mbs.AddObject(GenericJoint(markerNumbers=[mEngine, mGround], constrainedAxes=[1,1,1, 1,1,1],
151 visualization=VGenericJoint(show=False)))
152 sEngineForce = mbs.AddSensor(SensorObject(objectNumber=oEngineJoint, storeInternal=True,
153 outputVariableType=exu.OutputVariableType.ForceLocal))
154 sEngineTorque = mbs.AddSensor(SensorObject(objectNumber=oEngineJoint, storeInternal=True,
155 outputVariableType=exu.OutputVariableType.TorqueLocal))
156
157 bConrodList = []
158 bPistonList = []
159 gCrank = []
160 for cnt, angleCrank in enumerate(P.crankAngles):
161 anglePiston = P.pistonAngles[cnt]
162 Ac = RotationMatrixZ(angleCrank)
163 Ap = RotationMatrixZ(anglePiston)
164 [phi1,phi2, angleConrod, Acr, dp] = ComputeSliderCrank(angleCrank, anglePiston, P.crankArmLength, P.conrodLength)
165
166 zOff = -0.5*eL + cnt*P.pistonDistance + zOffAdd
167 #zOff = 0
168 #crank bearing
169 zAdd = 0
170 if cnt>0: zAdd = P.crankArmWidth
171 gCrank += [GraphicsDataCylinder(pAxis=[0,0,zOff-zAdd], vAxis=[0,0,P.crankBearingWidth+P.crankArmWidth+zAdd],
172 radius=P.crankBearingRadius, color=color4red)]
173 #arm1
174 arm1 = GraphicsDataOrthoCubePoint([P.crankArmLength*0.5,0,zOff+P.crankArmWidth*0.5+P.crankBearingWidth],
175 size=[P.crankArmLength,P.crankArmHeight,P.crankArmWidth], color=colorCrank)
176 gCrank += [MoveGraphicsData(arm1, [0,0,0], Ac)]
177 #conrod bearing
178 gCrank += [GraphicsDataCylinder(pAxis=Ac@[P.crankArmLength,0,zOff+P.crankBearingWidth+P.crankArmWidth*0],
179 vAxis=[0,0,P.conrodCrankCylLength+2*P.crankArmWidth], radius=P.conrodCrankCylRadius, color=colorCrank)]
180
181 #arm2
182 arm2 = GraphicsDataOrthoCubePoint([P.crankArmLength*0.5,0,zOff+P.crankArmWidth*1.5+P.crankBearingWidth+P.conrodCrankCylLength],
183 size=[P.crankArmLength,P.crankArmHeight,P.crankArmWidth],
184 color=colorCrank)
185 gCrank += [MoveGraphicsData(arm2, [0,0,0], Ac)]
186
187 if cnt == len(P.crankAngles)-1:
188 gCrank += [GraphicsDataCylinder(pAxis=[0,0,zOff+P.crankArmWidth+P.crankBearingWidth+P.conrodCrankCylLength], vAxis=[0,0,P.crankBearingWidth+P.crankArmWidth],
189 radius=P.crankBearingRadius, color=color4red)]
190
191 #++++++++++++++++++++++++++++++++++++++
192 #conrod
193 gConrod = [ GraphicsDataRigidLink (p0=[-0.5*P.conrodLength, 0, 0], p1=[0.5*P.conrodLength,0,0], axis0= [0,0,1], axis1= [0,0,1],
194 radius= [P.conrodRadius]*2,
195 thickness= P.conrodHeight, width=[P.conrodWidth]*2, color= colorConrod, nTiles= 16)]
196
197 [nConrod, bConrod] = AddRigidBody(mbs, P.inertiaConrod,
198 nodeType = nodeType,
199 position=Ac@[P.crankArmLength,0,0] + Acr@[0.5*P.conrodLength,0,
200 zOff+P.crankArmWidth+P.crankBearingWidth+0.5*P.conrodCrankCylLength],
201 # angularVelocity=[0,0,0],
202 rotationMatrix=Acr,
203 gravity = gravity,
204 graphicsDataList = gConrod
205 )
206 bConrodList += [bConrod]
207 #++++++++++++++++++++++++++++++++++++++
208 #piston
209 gPiston = [GraphicsDataCylinder(pAxis=[-P.conrodRadius*0.5,0,0],
210 vAxis=[P.pistonLength,0,0], radius=P.pistonRadius, color=colorPiston)]
211
212 [nPiston, bPiston] = AddRigidBody(mbs, P.inertiaPiston,
213 nodeType = nodeType,
214 # position=Ap@[P.crankArmLength + P.conrodLength,0,
215 # zOff+P.crankArmWidth+P.crankBearingWidth+0.5*P.conrodCrankCylLength],
216 position=Ap@[dp,0,
217 zOff+P.crankArmWidth+P.crankBearingWidth+0.5*P.conrodCrankCylLength],
218 # angularVelocity=[0,0,0],
219 rotationMatrix=Ap,
220 gravity = gravity,
221 graphicsDataList = gPiston
222 )
223 bPistonList += [bPiston]
224
225 [nCrank, bCrank] = AddRigidBody(mbs, P.inertiaCrank,
226 nodeType = nodeType,
227 position=[0,0,0],
228 #angularVelocity=[0,0,omega0],
229 gravity = gravity,
230 graphicsDataList = gCrank
231 )
232
233 sCrankAngVel = mbs.AddSensor(SensorNode(nodeNumber=nCrank, storeInternal=True,
234 outputVariableType=exu.OutputVariableType.AngularVelocity))
235
236 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
237 #JOINTS:
238 [oJointCrank, mBody0Crank, mBody1Crank] = AddRevoluteJoint(mbs, oEngine, bCrank, point=[0,0,-0.5*eL], axis=[0,0,1], showJoint=showJoints,
239 axisRadius=P.crankBearingRadius*1.2, axisLength=P.crankBearingWidth*0.8)
240
241 for cnt, angleCrank in enumerate(P.crankAngles):
242 anglePiston = P.pistonAngles[cnt]
243 Ac = RotationMatrixZ(angleCrank)
244 Ap = RotationMatrixZ(anglePiston)
245 [phi1,phi2, angleConrod, Acr, dp] = ComputeSliderCrank(angleCrank, anglePiston, P.crankArmLength, P.conrodLength)
246
247 zOff = -0.5*eL + cnt*P.pistonDistance
248 #zOff = 0
249
250 [oJointCC, mBody0CC, mBody1CC] = AddRevoluteJoint(mbs, bCrank, bConrodList[cnt],
251 point=Ac@[P.crankArmLength,0,zOff + P.crankBearingWidth+P.crankArmWidth+0.5*P.conrodCrankCylLength],
252 axis=[0,0,1], showJoint=showJoints,
253 axisRadius=P.crankBearingRadius*1.3, axisLength=P.crankBearingWidth*0.8)
254
255 #pPiston = A@[P.crankArmLength+P.conrodLength,0,zOff + P.crankBearingWidth+P.crankArmWidth+0.5*P.conrodCrankCylLength]
256 pPiston = Ap@[dp,0,zOff + P.crankBearingWidth+P.crankArmWidth+0.5*P.conrodCrankCylLength]
257 [oJointCP, mBody0CP, mBody1CP] = AddRevoluteJoint(mbs, bConrodList[cnt], bPistonList[cnt],
258 point=pPiston,
259 axis=[0,0,1], showJoint=showJoints,
260 axisRadius=P.crankBearingRadius*1.3, axisLength=P.crankBearingWidth*0.8)
261
262 # AddPrismaticJoint(mbs, oEngine, bPistonList[cnt],
263 # point=pPiston,
264 # axis=A@[1,0,0], showJoint=showJoints,
265 # axisRadius=P.crankBearingRadius*1.3, axisLength=P.crankBearingWidth*0.8)
266 mEngine = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oEngine, localPosition=pPiston))
267 mPiston = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bPistonList[cnt], localPosition=[0,0,0]))
268 mbs.AddObject(GenericJoint(markerNumbers=[mPiston, mEngine], constrainedAxes=[0,1,0, 0,0,1],
269 # rotationMarker0=A.T,
270 rotationMarker1=Ap,
271 visualization=VGenericJoint(show=False, axesRadius=P.conrodRadius*1.4,axesLength=0.05)))
272
273 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
274 #DRIVE:
275 def UFoffset(mbs, t, itemNumber, lOffset):
276 return 0
277
278 def UFoffset_t(mbs, t, itemNumber, lOffset): #time derivative of UFoffset
279 return SmoothStep(t, 0, 0.5, 0, omegaDrive)
280
281 mCrankRotation = mbs.AddMarker(MarkerNodeRotationCoordinate(nodeNumber=nCrank, rotationCoordinate=2))
282 mNodeEngine = mbs.AddMarker(MarkerNodeRotationCoordinate(nodeNumber=nEngine, rotationCoordinate=2))
283 oRotationConstraint = mbs.AddObject(CoordinateConstraint(markerNumbers=[mNodeEngine, mCrankRotation], velocityLevel=True,
284 offsetUserFunction=UFoffset,
285 offsetUserFunction_t=UFoffset_t,
286 visualization=VCoordinateConstraint(show=False)))
287
288 return [oEngine, oEngineJoint, sEngineForce, sEngineTorque, sCrankAngVel, oRotationConstraint, nCrank, bCrank]
289
290engines = []
291engines+=[EngineParameters([0])] #R1
292engines+=[EngineParameters([0,180])] #R2
293engines+=[EngineParameters([0,180,180,0])] #R4 straight-four engine, Reihen-4-Zylinder
294engines+=[EngineParameters([0,90,270,180])] #R4 in different configuration
295engines+=[EngineParameters([0,180,180,0],[0,180,180,0])] #Boxer 4-piston perfect mass balancing
296
297engines+=[EngineParameters([0,120,240])] #R3
298engines+=[EngineParameters(list(np.arange(0,5)*144))] #R5
299engines+=[EngineParameters([0,120,240,240,120,0])] #R6
300engines+=[EngineParameters([0,0,120,120,240,240],[-30,30,-30,30,-30,30])] #V6
301engines+=[EngineParameters([0,0,120,120,240,240,240,240,120,120,0,0],[-30,30,-30,30,-30,30,30,-30,30,-30,30,-30])] #V12
302
303engines+=[EngineParameters([0,90,180,270,270,180,90,360])] #R8
304engines+=[EngineParameters([0,0,90,90,270,270,180,180], [-45,45,-45,45, 45,-45,45,-45])] #V8
305
306# n=12
307# a=list(np.arange(0,n)*30)
308# b=list(np.arange(n-1,-1,-1)*30)
309# #engines+=[EngineParameters(a+a,b+b)
310
311#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
312#
313engines=[EngineParameters([0,90,270,180], [90]*4)]
314#engines=[EngineParameters([0,0,120,120,240,240,240,240,120,120,0,0],[60,120,60,120,60,120,120,60,120,60,120,60])] #V12
315
316for engine in engines:
317
318 SC = exu.SystemContainer()
319 mbs = SC.AddSystem()
320
321 [oEngine, oEngineJoint, sEngineForce, sEngineTorque, sCrankAngVel, oRotationConstraint,
322 nCrank, bCrank] = CreateEngine(engine)
323
324 d = 2.4 #box size
325 h = 0.5*d #box half size
326 w = d
327 gDataList = []
328 gDataList += [GraphicsDataCheckerBoard(point=[0,0,-h], normal=[0,0,1], size=2*d, size2=d, nTiles=12*2, nTiles2=12, color=color4grey)]
329 gDataList += [GraphicsDataCheckerBoard(point=[-w,0,0], normal=[ 1,0,0], size=d, nTiles=12, color=color4lightgrey)]
330 gDataList += [GraphicsDataCheckerBoard(point=[ w,0,0], normal=[-1,0,0], size=d, nTiles=12, color=color4lightgrey)]
331 gDataList += [GraphicsDataCheckerBoard(point=[0,-h,0], normal=[0,-1,0], size=2*d, size2=d, nTiles=12*2, nTiles2=12, color=color4dodgerblue)]
332 gDataList += [GraphicsDataCheckerBoard(point=[0, h,0], normal=[0, 1,0], size=2*d, size2=d, nTiles=1, color=[0.8,0.8,1,1])]#, alternatingColor=[0.8,0.8,1,1])]
333 # gDataList += [GraphicsDataCheckerBoard(point=[0, 0,h], normal=[0, 0,-1], size=d, nTiles=1, color=[0.8,0.8,0.8,0.9])]
334
335 oGround=mbs.AddObject(ObjectGround(referencePosition= [0,0,0],
336 visualization=VObjectGround(graphicsData=gDataList)))
337
338
339 def PreStepUF(mbs, t):
340 u = mbs.systemData.GetODE2Coordinates()
341
342 if not fixedSpeed and t >= 1: #at this point, the mechanism runs freely
343 mbs.SetObjectParameter(oRotationConstraint, 'activeConnector', False)
344
345 #mbs.systemData.SetODE2Coordinates(u)
346 return True
347
348 mbs.SetPreStepUserFunction(PreStepUF)
349
350
351 mbs.Assemble()
352
353 stepSize = 0.002
354 simulationSettings = exu.SimulationSettings() #takes currently set values or default values
355
356 simulationSettings.timeIntegration.numberOfSteps = int(tEnd/stepSize)
357 simulationSettings.timeIntegration.endTime = tEnd
358 # simulationSettings.timeIntegration.newton.relativeTolerance = 1e-8*0.01
359 # simulationSettings.timeIntegration.newton.absoluteTolerance = 1e-10*0.01
360 simulationSettings.timeIntegration.verboseMode = 1
361
362 # simulationSettings.timeIntegration.simulateInRealtime = True
363
364 simulationSettings.solutionSettings.solutionWritePeriod=0.01
365 simulationSettings.solutionSettings.sensorsWritePeriod = stepSize*10
366 simulationSettings.solutionSettings.writeSolutionToFile = False
367 #simulationSettings.solutionSettings.writeInitialValues = False #otherwise values are duplicated
368 #simulationSettings.solutionSettings.coordinatesSolutionFileName = 'solution/coordinatesSolution.txt'
369
370 simulationSettings.timeIntegration.generalizedAlpha.computeInitialAccelerations = False
371
372 simulationSettings.timeIntegration.generalizedAlpha.lieGroupAddTangentOperator = False
373 #simulationSettings.displayStatistics = True
374 # simulationSettings.displayComputationTime = True
375 simulationSettings.linearSolverType=exu.LinearSolverType.EigenSparse
376
377 #SC.visualizationSettings.nodes.defaultSize = 0.05
378
379 simulationSettings.solutionSettings.solutionInformation = "Engine"
380
381 SC.visualizationSettings.general.graphicsUpdateInterval = 0.01
382 #SC.visualizationSettings.general.drawWorldBasis = True
383 #SC.visualizationSettings.general.worldBasisSize = 0.1
384
385 SC.visualizationSettings.markers.show = False
386 SC.visualizationSettings.loads.show = False
387 SC.visualizationSettings.nodes.show = False
388 SC.visualizationSettings.connectors.show = False
389
390 SC.visualizationSettings.openGL.multiSampling = 4
391 SC.visualizationSettings.openGL.shadow = 0.3 #set to 0, if your graphics card cannot handle this!
392 SC.visualizationSettings.openGL.lineWidth = 3
393 SC.visualizationSettings.openGL.light0position = [0.25,1,3,0]
394
395 #++++++++++++++++++++++++++++++++
396 #openVR:
397 SC.visualizationSettings.general.drawCoordinateSystem = False
398 #good for openVR
399 SC.visualizationSettings.general.graphicsUpdateInterval = 0.005 #small enough to get large enough fps
400 simulationSettings.timeIntegration.simulateInRealtime = True
401
402 useOpenVR = False #set this true for openVR to run!!!
403 SC.visualizationSettings.window.renderWindowSize=[1176, 1320] # this needs to fit to your VR HMD (Head Mounted Display) settings (will show in console when openVR is started and openVR.logLevel is large enough!)
404 if useOpenVR:
405 SC.visualizationSettings.openGL.initialZoom = 1# 0.4*20 #0.4*max scene size
406 #SC.visualizationSettings.openGL.initialCenterPoint = [0,0,2]
407 SC.visualizationSettings.general.autoFitScene = False
408 SC.visualizationSettings.window.limitWindowToScreenSize = False #this allows a larger window size than your monitor can display in case!
409 SC.visualizationSettings.window.startupTimeout = 100000 #if steam / VRidge, etc. not found
410 SC.visualizationSettings.interactive.openVR.enable = True
411 SC.visualizationSettings.interactive.lockModelView = True #lock rotation/translation/zoom of model
412 SC.visualizationSettings.interactive.openVR.logLevel = 3
413 SC.visualizationSettings.interactive.openVR.actionManifestFileName = "C:/DATA/cpp/DocumentationAndInformation/openVR/hellovr_actions.json"
414
415 #%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
416
417
418 SC.visualizationSettings.general.autoFitScene = False #use loaded render state
419 exu.StartRenderer()
420 cws = SC.GetRenderState()['currentWindowSize']
421 print('window size=', cws, '(check that this is according to needs of Head Mounted Display)')
422 # if 'renderState' in exu.sys:
423 # SC.SetRenderState(exu.sys[ 'renderState' ])
424
425 mbs.SolveDynamic(simulationSettings)
426
427 exu.StopRenderer() #safely close rendering window!