Opening a second window just make inputs impossible

This code works pretty well,boxNP moves according to inputs. Just uncomment the Second window setup part in the __init__ then the second window is opened. But then inputs stops working.

    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)

    inputState.watchWithModifiers('forward', 'w')
    inputState.watchWithModifiers('left', 'a')
    inputState.watchWithModifiers('reverse', 's')
    inputState.watchWithModifiers('right', 'd')
    inputState.watchWithModifiers('turnLeft', 'q')
    inputState.watchWithModifiers('turnRight', 'e')

Objects don’t move and other inputs doesn’t work either. Is this a known issue or limitation or am I make a mistake.Any idea appreciated, I’m about to give up.
Thank you.

#setCollideMask not working make it work for more than one box for training


import sys
#import direct.directbase.DirectStart

from direct.showbase.DirectObject import DirectObject
from direct.showbase.InputStateGlobal import inputState

from panda3d.core import AmbientLight
from panda3d.core import DirectionalLight
from panda3d.core import Vec3
from panda3d.core import Vec4
from panda3d.core import Point3
from panda3d.core import TransformState
from panda3d.core import BitMask32

from panda3d.bullet import BulletWorld
from panda3d.bullet import BulletPlaneShape
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
from panda3d.bullet import BulletDebugNode



#NK
from panda3d.core import WindowProperties
from panda3d.core import NodePath, Camera, PerspectiveLens
from direct.showbase.ShowBase import ShowBase
from panda3d.core import DisplayRegion
from panda3d.core import GraphicsOutput, MouseWatcher
from direct.showbase.ShowBaseGlobal import globalClock

import torch
import torch.nn as nn
base = ShowBase()


class Game(DirectObject):

  def __init__(self):
# First window setup
    wp = WindowProperties()
    wp.setSize(960, 540)
    wp.setOrigin(0, 0)
    wp.title = 'First'
    wp.setForeground(True)  # Keep main window focused
    base.win.requestProperties(wp)
    
    # Second window setup
    # wp2 = WindowProperties()
    # wp2.setSize(800, 600)
    # wp2.setOrigin(960, 0)
    # wp2.title = 'Second'
    # # wp2.setForeground(True)  # REMOVE THIS LINE
    
    # # Open second window
    # win2 = base.openWindow(props=wp2, makeCamera=True)
    
    # Force main window to retain focus (optional but recommended)
   
    
 
    self.render = base.render
    self.loader = base.loader

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)

    # Input
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)

    inputState.watchWithModifiers('forward', 'w')
    inputState.watchWithModifiers('left', 'a')
    inputState.watchWithModifiers('reverse', 's')
    inputState.watchWithModifiers('right', 'd')
    inputState.watchWithModifiers('turnLeft', 'q')
    inputState.watchWithModifiers('turnRight', 'e')
    taskMgr = base.taskMgr
    # Task
    taskMgr.add(self.update, 'updateWorld')

    
    # Physics
    self.setup()

  # _____HANDLER_____


  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setup()

  def toggleWireframe(self):
    base.toggleWireframe()

  def toggleTexture(self):
    base.toggleTexture()

  def toggleDebug(self):
    if self.debugNP.isHidden():
      self.debugNP.show()
    else:
      self.debugNP.hide()

  def doScreenshot(self):
    #base.screenshot('Bullet')
    self.reg.saveScreenshot("screenshot.jpg")
    #DisplayRegion.saveScreenshot(self.reg,"screenshot.jpg")
  # ____TASK___

  def processInput(self, dt):
    force = Vec3(0, 0, 0)
    torque = Vec3(0, 0, 0)

    if inputState.isSet('forward'): force.setY( 1.0)
    if inputState.isSet('reverse'): force.setY(-1.0)
    if inputState.isSet('left'):    force.setX(-1.0)
    if inputState.isSet('right'):   force.setX( 1.0)
    if inputState.isSet('turnLeft'):  torque.setZ( 1.0)
    if inputState.isSet('turnRight'): torque.setZ(-1.0)

    force *= 30.0
    torque *= 2.0

    force = self.render.getRelativeVector(self.boxNP, force)
    torque = self.render.getRelativeVector(self.boxNP, torque)
    
    self.boxNP.node().setActive(True)
    self.boxNP.node().applyCentralForce(force)
    self.boxNP.node().applyTorque(torque)

  def update(self, task):
    dt = globalClock.getDt()
    
    
    self.processInput(dt)
    #self.world.doPhysics(dt)
    self.world.doPhysics(dt, 5, 1.0/180.0)
    #self.collisionDetector()

    return task.cont

  def cleanup(self):
    self.world.removeRigidBody(self.groundNP.node())
    self.world.removeRigidBody(self.boxNP.node())
    self.world = None

    self.debugNP = None
    self.groundNP = None
    self.boxNP = None

    self.worldNP.removeNode()

  def setup(self):
    self.worldNP = self.render.attachNewNode('World')
    
    # World
    self.debugNP = self.worldNP.attachNewNode(BulletDebugNode('Debug'))
    self.debugNP.show()
    self.debugNP.node().showWireframe(True)
    self.debugNP.node().showConstraints(True)
    self.debugNP.node().showBoundingBoxes(False)
    self.debugNP.node().showNormals(True)
    

   

    self.world = BulletWorld()
    
    self.world.setGravity(Vec3(0, 0, -9.81))
    self.world.setDebugNode(self.debugNP.node())

    # Ground (static)
    shape = BulletPlaneShape(Vec3(0, 0, 1), 1)

    self.groundNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Ground'))
    self.groundNP.node().addShape(shape)
    self.groundNP.setPos(0, 0, -2)
    self.groundNP.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(self.groundNP.node())
    
    # Box (dynamic)
    shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

    self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
    self.boxNP.node().setMass(1.0)
    self.boxNP.node().addShape(shape)
    self.boxNP.setPos(0, 0, 2)
    #self.boxNP.setScale(2, 1, 0.5)
    #self.boxNP.setCollideMask(BitMask32.allOn())
    
    #self.boxNP.node().setDeactivationEnabled(False)

    self.world.attachRigidBody(self.boxNP.node())
    self.boxNP.setCollideMask(BitMask32.bit(0))
    #visualNP = self.loader.loadModel('models/box.egg')
    visualNP = self.loader.loadModel('minecraft_blocks/sand-block.glb')

    visualNP.setScale(0.5)
    visualNP.clearModelNodes()
    visualNP.reparentTo(self.boxNP)


    shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
    self.grassBox = self.worldNP.attachNewNode(BulletRigidBodyNode('grassBox'))
    self.grassBox.setCollideMask(BitMask32.bit(1))
    # self.grassBox.node().setMass(1.0)
    self.grassBox.node().addShape(shape)
    self.grassBox.setPos(2, 2, -0.5)
    #self.grassBox.setScale(2, 1, 0.5)
    
    
    #self.grassBox.node().setDeactivationEnabled(False)

    self.world.attachRigidBody(self.grassBox.node())
    
    visualgrassBoxNP = self.loader.loadModel('minecraft_blocks/grass-block.glb')
    visualgrassBoxNP.setScale(0.5)
    visualgrassBoxNP.clearModelNodes()
    visualgrassBoxNP.reparentTo(self.grassBox) 
    
    
    # win2 = base.openWindow(makeCamera=False,props=wp, aspectRatio=16/9,name = "hi")
  
    # c2 = base.render.attach_new_node(Camera("C2"))
    # c2.setPos(0, -0.5, 2)
    # c2.setHpr(1, 100, 100)
    # c2.node().setLens(PerspectiveLens())
    # c3 = base.render.attach_new_node(Camera("C3"))
    # dr2 = win2.makeDisplayRegion(0.5, 1, 0.5, 1)
    # dr2.set_camera(c2)
    # dr3 = win2.makeDisplayRegion(0, 0.5, 0.5, 1)
    # dr3.set_camera(c3)

  def collisionDetector(self):
    #
    result = self.world.contactTestPair(self.grassBox.node(), self.boxNP.node())
    
    print(result.getNumContacts())

    for contact in result.getContacts():
      print(contact.getNode0())
      print(contact.getNode1())

      mpoint = contact.getManifoldPoint()
      print(mpoint.getDistance())
      print(mpoint.getAppliedImpulse())
      print(mpoint.getPositionWorldOnA())
      print(mpoint.getPositionWorldOnB())
      print(mpoint.getLocalPointA())
      print(mpoint.getLocalPointB())
  
    # my_cam.node().setActive(True)
    # base.cam.node().setActive(False)
    # camNP = NodePath(my_cam)


    # # print(base.win.getNumDisplayRegions())
    # # print(base.win.getDisplayRegions())
    # # print(base.win.active_display_regions)

    # # print(win2.getNumDisplayRegions())
    # # print(win2.getDisplayRegions())
    # # print(win2.active_display_regions)
    # # win2.active_display_regions[0].setCamera(camNP)
    
    # region=base.win.makeDisplayRegion(0, 0.25, 0, 0.25)
    # region.setCamera(camNP)
    # region.setSort(10)
    # self.reg=region
    

    
    # camNP.reparentTo(visualNP)
    #c2.reparentTo(visualNP) 
# region=base.win.makeDisplayRegion()
# camNode = Camera('cam')
# camNP = NodePath(camNode)

# region.setCamera(camNP)
# camNP.reparentTo(self.render)
# this is contact detector.
   
  def collisionDetector(self):
    # body1 = BulletRigidBodyNode("body1")   

    # body2 = BulletRigidBodyNode("body2")
    

    #result = self.world.contactTest(self.grassBox.node())
    result = self.world.contactTestPair(self.grassBox.node(), self.boxNP.node())
    
    print(result.getNumContacts())

    for contact in result.getContacts():
      print(contact.getNode0())
      print(contact.getNode1())

      mpoint = contact.getManifoldPoint()
      print(mpoint.getDistance())
      print(mpoint.getAppliedImpulse())
      print(mpoint.getPositionWorldOnA())
      print(mpoint.getPositionWorldOnB())
      print(mpoint.getLocalPointA())
      print(mpoint.getLocalPointB())
  
game = Game()
base.run()

You should call the method for the second window, base.setup_mouse() to make it active for input.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties

class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)

        self.win_focus = None

        scene = loader.load_model("models/environment")
        scene.reparent_to(render)

        window_properties = WindowProperties()
        window_properties.set_size(800, 600)
        window_properties.set_title('win2')

        self.win2 = self.open_window(props = window_properties)

        taskMgr.add(self.focus_task, "focus_task")

        base.accept("enter", self.test)

    def test(self):
        if self.win_focus != None:
            print(self.win_focus.name)

    def focus_task(self, task):
        if self.win.get_properties().get_foreground() == True:
            self.win_focus = base.win
            base.setup_mouse(base.win)

        if self.win2.get_properties().get_foreground() == True:
            self.win_focus = self.win2
            base.setup_mouse(self.win2)

        return task.cont

app = MyApp()
app.run()
1 Like

Thank you for your help, @serega-kkz. I was really stuck at that point, and your solution helped me move forward.
Now, at least, inputs like base.accept("enter", self.test) work on one of the foreground windows (win2). I believe that is the intended behavior.

At this point, what I don’t fully understand is why we need the focus_task method. I believe you created it to automate switching back and forth, but it never switches back to the first window anyway. The test function only works for win2.

Again, I’m good to go with this solution, and thank you. If you provide more information, I’d be willing to contribute to the documentation(or a mini tutorial) for it.

Yes, I created the focus_task task to automatically switch the active window, but it works fine, here’s the output.

  wglGraphicsPipe
(all display modules loaded.)
window1
window1
window1
window2
window2
window2
window2
window1
window1
window1
window2
window2
window2
window2

When I change the window (the active window), I get different names. Maybe it only works on Windows…

1 Like

Yes probably, here is my output. I guess there is an error here.

/home/niyazi/miniforge3/bin/python3.10 /home/niyazi/Git/panda3d_experiment/window_test.py 
Known pipe types:
  glxGraphicsPipe
(1 aux display modules not yet loaded.)
window2
window2
window2
window2
window2
window2
window2
window2

Maybe the problem is here: I’ll check this glxGraphicsPipe (1 aux display modules not yet loaded.) it was always there.Now I realized.