a drag box for selecting an area

Yes, that’s exactly what lens.extrude() does. You do have to take care about the coordinate space you’re working in. Try search the forums for “extrude”, you’ll find plenty of examples of people using it to map 2-d coordinates to 3-d.

As to pickerRay.setFromLens(), this is implemented in C++, as CollisionRay::set_from_lens() (following the C++/Python renaming convention). It’s basically this code:

  if (lens->extrude(point, near_point, far_point)) {
    _origin = near_point;
    _direction = far_point - near_point;
  }

David

Well, I made it as far as this:

		a, b, c= Point3( ), Point3( ), Point3( )
		base.cam.node( ).getLens( ).extrude( Point2( x, y ), a, b )
		a= NodePath( ).getRelativePoint( base.cam, a )
		b= NodePath( ).getRelativePoint( base.cam, b )
		p=Plane( Point3( 0,0,0 ), Point3( 1,0,0 ), Point3( 0,0,1 ) )
		p.intersectsLine( c, a, b )
		return c

That’s pretty satisfactory. It does appear more general than the above version. I wonder a bit about the blank NodePath constructor but conceptually it’s what I want.

This one is more mysterious. At some distances, my textures are invisible, but then at farther distances, they are visible again. It isn’t critical to my project but it is a curiosity. Is it just an artifact / technical detail? Furthermore, some linesegs are visible while others aren’t at the same depth, and different ones are visible at different X-Z coordinates.

Y-coordinate of camera, textures visible
-369.474731445 on
-447.848144531 off
-542.846252441 off
-657.995483398 on

An empty NodePath used in a relative operation stands for the top of the graph, so this is perfectly legit.

Here I’ve got no ideas. There’s nothing in Panda that does this sort of thing automatically, so it must be something unique to your environment. It sounds like it might be an issue with the bounding volumes. Normally, Panda computes the bounding volume for an object automatically, and uses this volume to determine whether to render the object. When you are dealing with dynamic geometry, though, that you create yourself, or that you animate via the RBC, then Panda isn’t able to compute a reliable bounding volume automatically, so it becomes your responsibility to place a good bounding volume over the whole lot of them.

One easy cop-out is to place an infinite bounding volume over all of your dynamic geometry:

root.node().setBounds(OmniBoundingVolume())
root.node().setFinal(True)

David

No dice on that lead. I put it on the rbc node, not the path, right after rbc.collect( ).

I offer to try to get a repro for you.

In this version,

-121.92666626 on
-147.789901733 off
-179.139282227 off
-217.138519287 on

However,

-217.13848877 off 1st
-263.198181152 off 1st

are also off on the first time through, but do appear after we have zoomed back out and start to zoom back in on a 2nd pass.

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from panda3d.core import NodePath
from panda3d.core import CardMaker
from panda3d.core import AmbientLight
from panda3d.core import Vec4

cm = CardMaker('card')
card1 = cm.generate()
path1= NodePath( card1 )
path1.reparentTo( render )
path1.setColor( 1, 1, 1, 1 )

alight = AmbientLight('alight')
alight.setColor(Vec4(1,1,1,1))
alnp = render.attachNewNode(alight)
render.setLight(alnp)

base.cam.setPos( 0, -10, 0 )
base.cam.node().getLens().setNear( 1e-5 )

class Diagram(DirectObject):
	def __init__(self):
		self.accept("page_up", self.zoom1)
		self.accept("page_down", self.zoom2)
		self.accept("page_up-repeat", self.zoom1)
		self.accept("page_down-repeat", self.zoom2)
		self.accept("wheel_up", self.zoom1)
		self.accept("wheel_down", self.zoom2)
	def zoom1( self ):
		c= base.cam
		c.setY( c.getY( )* .825 )
		print c.getY( )
	def zoom2( self ):
		c= base.cam
		c.setY( c.getY( )/ .825 )
		print c.getY( )

dag= Diagram( )

run( )

So in this simple program, the white card disappears at certain distances, and then reappears as you get farther away?

I certainly don’t see that behavior when I run this program. The white card is there the whole time, it just gets smaller and smaller.

Maybe it’s a bug in your graphics drivers? What happens if you run with tinydisplay (“load-display tinydisplay”)?

Incidentally, although this certainly isn’t related to any issue you’re talking about, it isn’t necessary to create an AmbientLight of (1, 1, 1, 1) in Panda. This is effectively what Panda does by default when you have no lights at all.

David

If you run with tinydisplay (“load-display tinydisplay”), the white card disappears at getZ = 20, and doesn’t even flicker back.

My driver is: 6.14.10.4609 (6-6-2006)

Ah, why so it does. I see what is happening now. This is an artifact of very poor Z resolution, caused by setting the near plane too small.

Certain graphics drivers have enough Z resolution that it’s not an issue, even with such a close near plane. Yours evidently doesn’t. :slight_smile:

Try setting the near plane to something more like 0.1.

David

That was successful. Since I know the plane I need in view, I’ll just set the near and far clipping planes on either side of it.

Is that going to affect performance, or can I reset them on every Y-move?

Hmm, I think you can reset them every frame with no significant performance cost.

David

I switched to an Orthographic lens. I’m setting the film size from a zoom factor I’m maintaining as an attribute of a class. As a result, I’m not manipulating the Y-position at all, so extrude doesn’t work. To complicate matters, I have multiple display regions, so the center of the region isn’t the center of the screen.

How am I supposed to determine the model coordinates of the mouse? As above, the coordinates of interest are on the XZ-plane, Y = 0, which is orthogonal to the view vector.

I’m not sure why extrude shouldn’t work on the Orthagonal lens–in this case, all of the extrusions are parallel with the view vector, but they’re still perfectly valid rays.

As to the DisplayRegion, you could convert the coordinates yourself from the local DisplayRegion coordinates to the appropriate screen-wide MouseWatcherRegion coordinates. Or, if you don’t want to mess with that, you can call MouseWatcher.setRegion(myDisplayRegion) to automatically constrain the MouseWatcher to that particular DisplayRegion, and recalculate coordinates appropriately. But then you have to have a separate MouseWatcher for each DisplayRegion (which is an option).

David

I’m still not totally clear on this. Here’s what I have.

EDIT: (From the init method of the View class.)

self.lens= OrthographicLens( )
self.cam= Camera( 'cam', self.lens )
self.dispreg= base.win.makeDisplayRegion( *rect )
self.camP= NodePath( self.cam )
self.dispreg.setCamera( self.camP )
self.camP.reparentTo( render )
self.camP.setPos( *pos0 )
self.rezoom( zoom0 )
self.mousereg= MouseWatcherRegion( 'region-%i'%i, *[ x*2-1 for x in rect ] )
base.mouseWatcherNode.addRegion( self.mousereg )

‘rezoom’ just sets the film size (as a multiple of the display region size in pixels).

Are you saying I’ll need an additional MouseWatcher object? That is, additionally to the MouseWatcherRegion? How will it be structured in the scene graph in relation to other MouseWatchers, and base.mouseWatcherNode?

Sorry, I misled you. All you need to call is:

base.mouseWatcherNode.setDisplayRegion(self.dispreg)

This will change the default MouseWatcher (which is base.mouseWatcherNode) to use your custom DisplayRegion, instead of the full-screen DisplayRegion.

It is also an option to create multiple MouseWatchers, each assigned to a different DisplayRegion. These new MouseWatchers would be siblings of base.mouseWatcherNode. This is probably more complex than you need, though.

None of this is directly related to the MouseWatcherRegions you are creating. A MouseWatcher is a node that sits in the datagraph and watches the mouse move around. It has an associated DisplayRegion, and a set of MouseWatcherRegions. If you created a second MouseWatcher, you could associate a second DisplayRegion simultaneously. You would then have to be careful which MouseWatcher you added each corresponding MouseWatcherRegion to.

David

I’m still lost and unfortunately my last post didn’t help any. It was from the init method of the ‘View’ class, which creates one view of the main model. So, multiple display regions will all be on different instances of view. Because of that I’m having trouble making sense of calling ‘base.mouseWatcherNode.setDisplayRegion(self.dispreg)’. Wouldn’t a second call overwrite the setting from the first call?

It makes sense that each View would be siblings of eachother, along with their display regions, mousewatchers, mousewatchernodes, and mousewatchergroups. If there’s a way to simplify it then I am not seeing it at all.

Ah, since you want to have multiple DisplayRegions all simultaneously containing MouseWatcherRegions, you have two choices: (1) create multiple MouseWatchers, one for each DisplayRegion, or (2) keep one MouseWatcher, but convert all of your MouseWatcherRegions into the appropriate space for each DisplayRegion.

(1) is possible, but relatively complex, because it means you need to take over the code from ShowBase that’s creating only one MouseWatcher by default. I would guess that (2) is much simpler.

(2) just means that you transform the coordinates by something other than (x)*2-1; it will be different for each DisplayRegion.

David

On the idea that sharing one’s doubts actually aids in solving them, I’m going to broadcast loudly to the world:

#1 sounds more complex!!! Yet better.

Well, let me not dissuade you from trying (1), then. I’d recommend first trying it with the one DisplayRegion, to ensure that mouseWatcherNode.setDisplayRegion() works as expected.

With that confidence, you can fell free to create as many MouseWatcher’s as you want. Parent each of them to the same parent as base.mouseWatcherNode (or base.mouseWatchers[0]), which is to say, the MouseAndKeyboard node in the data graph.

You may also want to create a ButtonThrower for each MouseWatcher, and attach it as a child to each one. You can see the code in ShowBase that sets all of this stuff up.

The idea of the datagraph is this: data (usually input data from the user, such as mouse tracking and keyboard events) flows down the graph from parents to children. Nodes in the data graph either produce data (MouseAndKeyboard), consume data (ButtonThrower, Trackball) or somehow process data and pass it through (MouseWatcher).

So the MouseAndKeyboard object generates all of the data corresponding to the raw input devices. The MouseWatcher node sits directly under that node, and watches the mouse move around, making note of which MouseWatcherRegions the mouse is crossing. The MouseWatcher also passes its input data through, though I believe it scales mouse data to fill the DisplayRegion it’s set to. Beneath the MouseWatcher sits the ButtonThrower node, which generates the events that we’re used to listening for when keypresses and mouse buttons are clicked (e.g. ‘f1’, ‘a-up’, and so on).

When the trackball is enabled, the Trackball node also sites below the MouseWatcher, and moves the camera (or an arbitrary object in the scene, if desired) around according to the motions described by the mouse.

David

Every “view” has a display region, mouse watcher, and mouse watcher region. To get the highlight box, I need base.MWN.getMouse, but then to get the world coordinates, I need view.MWN.getMouse.

I’m having trouble routing the ‘mouse-up’ event to the MWN that got the ‘mouse-down’ event.

My aspect ratio doesn’t respond automatically when I resize the window. Now I will have to trap the resize event. Or, can I use a different strategy than setting the film size to zoom?

This version of the View class is >100 lines. It handles left-button selection, right-button dragging, and wheel zoom. Apparently, I am opposed to 100-line snippets on the forum, or I don’t have the enthusiasm for getting it in pastebin. But here’s the init.

class View( ):
	def __init__( self, i, rect, zoom0, pos0 ):
		self.lens= OrthographicLens( )
		self.cam= Camera( 'cam', self.lens )
		self.dispreg= base.win.makeDisplayRegion( *rect )
		self.camP= NodePath( self.cam )
		self.dispreg.setCamera( self.camP )
		self.camP.reparentTo( render )
		self.camP.setPos( *pos0 )
		self.rezoom( zoom0 )
		self.mousereg= MouseWatcherRegion( 'region-%i'%i, *[ x*2-1 for x in rect ] )
		base.mouseWatcherNode.addRegion( self.mousereg )
		self.mousewatN= MouseWatcher( 'mousewatcher-%i'%i )
		self.mousewatN.setDisplayRegion( self.dispreg )
		self.mousewat= NodePath( self.mousewatN )
		self.mousewat.reparentTo( base.mouseWatcher )