1
$\begingroup$

I made this toy example to demonstrate the problem i have. I am trying to use BGE to make screenshots of a rotating object for a report. I use a script on the cube to rotate and capture that uses logic to run on the object (e.g. deleted the cube and added a rotated cone - to which I applied the rotation, so in the screenshot it has rotation 0):

enter image description here

import bpy
import bge
from mathutils import *
import numpy as np
import math


cont = bge.logic.getCurrentController()
own = cont.owner
new_q = np.random.randint(4)
if new_q == 0:
    q = Euler((0.0, 0.0, 0.0)).to_quaternion()    
elif new_q == 1:
    q = Euler((0.0, 0.0, math.radians(90.0))).to_quaternion()    
elif new_q == 2:
    q = Euler((0.0, 0.0, math.radians(180.0))).to_quaternion()    
elif new_q == 3:
    q = Euler((0.0, 0.0, math.radians(270.0))).to_quaternion()    
own.worldOrientation = q
fname = 'e:/temp/q_{}-{}.png'.format(new_q,int(np.random.random() * 100000))
ivp = bge.texture.ImageViewport()
ivp.refresh()
print ('saving {}'.format(fname))
bge.render.makeScreenshot(fname)

the script rotates the object and then captures a screenshot: However, when I compare the screenshots, they are not acurate. E.g., files starting with q_0 randomly point to all axes...

Is there any way to enforce a new render so that the saved images have the exact rotation they are supposed to have?

I also tried saving on the next frame (e.g. saving to the global dictionary a flag, and then on the next time the script was called, save then) No joy there either... :S

$\endgroup$
  • $\begingroup$ Side note: do not import bpy. This is the Blender API but you are working with the BGE (not with Blender). You will get problems when you publish your game. $\endgroup$ – Monster May 26 '17 at 4:28
1
$\begingroup$

BGE API:

bge.render.makeScreenshot(filename)

Writes an image file with the current displayed frame

Your changes to the scene will be rendered after all logic of the current frame completed. Regardless what you do and in what order the rotation will not be part of the already rendered output of the last frame.

Your idea to perform logic (rotation) in one frame and call makeScreenshot within the next one is correct.

I strongly suggest to separate these two operations. Rotating an object and making a screenshot are two separate operations. As they need to run at different time points running it from the same controller makes the design unnecessary complex.

As you want to create the filename dependent on a previously chosen number I suggest to transfer this number somehow to the other processing. In this case I suggest to send it via message. This serves two puposes:

  • tells when to make a screen shot
  • provides information necessary to create the file name

I would not send the file name as it is the responsibility of the saving operation to care this detail, while the rotation processing cares just the random selection (it selects an option not a file name).

Randomly Turn

enter image description here

turnRandomly

import bge
from mathutils import Euler
import random
import math


controller = bge.logic.getCurrentController()

if all(sensor.positive for sensor in controller.sensors):
    owner = controller.owner
    new_q = random.randint(0,3)
    if new_q == 0:
        q = Euler((0.0, 0.0, 0.0)).to_quaternion()    
    elif new_q == 1:
        q = Euler((0.0, 0.0, math.radians(90.0))).to_quaternion()    
    elif new_q == 2:
        q = Euler((0.0, 0.0, math.radians(180.0))).to_quaternion()    
    elif new_q == 3:
        q = Euler((0.0, 0.0, math.radians(270.0))).to_quaternion()    
    owner.worldOrientation = q

    for actuator in controller.actuators:
        try:
            actuator.body = str(new_q)
        except AttributeError:
            pass
        controller.activate(actuator)

Assumptions:

  • all sensors needs to be positive (AND behavior)
  • should have a message actuator (otherwise there will be no error but make screenshot will not be triggered)
  • all actuators will be activated

    Make Screenshot

enter image description here

makeScreenshot

import bge
from datetime import datetime

controller = bge.logic.getCurrentController()
if all(sensor.positive for sensor in controller.sensors):
    for sensor in controller.sensors:
        try:
           bodies = sensor.bodies
        except AttributeError:
            continue

        for body in bodies:
            option = body
            filename = 'e:/temp/q_{}-{:%Y%m%d-%H%M%S}.png'.format(
                option, datetime.now())
            print ('saving {}'.format(filename))
            bge.render.makeScreenshot(filename)

assumptions:

  • all sensors needs to be positive (AND behavior)
  • non-message sensors will only be checked without any further processing
  • all bodies will be processed (e.g. several messages arrive at the same frame)
  • the screenshot will use a timestamp in name (rather than a non-unique random number)

As the message will trigger one frame later, the screenshot will be made a the right frame.

Be aware these operations do not need to be performed by the same object.

$\endgroup$
  • $\begingroup$ Thanx for your answer! (+1) I ll go check it to make sure it works, then accept... $\endgroup$ – ntg May 30 '17 at 7:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.