def centerOnPoint(self, pos, distance=None): #self.rotateView(45., math.degrees(math.atan(1/math.sqrt(2)))) vec = self.cameraVector() ray = Ray(pos, -vec) newPos = ray.atHeight(self.hoverHeight) log.info("Iso: centering on %s (moving to %s)", pos, newPos) self.centerPoint = newPos
def drawSelf(self): if self.sceneNode.corners is None or self.sceneNode.dimension is None: return corners = self.sceneNode.corners outwardSegments = [ LineSegment(corners[i], corners[j]) for i, j in self.outwardIndices ] verticalSegments = [ LineSegment(corners[i], corners[j]) for i, j in self.verticalIndices ] points = [] for segment in outwardSegments: p = segment.atHeight(self.sceneNode.planeHeight) if p is not None: points.append(p) if len(points) < 4: # only intersected two outward segments. check the far verticals. for segment in verticalSegments[:2]: r = Ray.fromPoints(*segment) points.append(r.atHeight(self.sceneNode.planeHeight)) if len(points) < 4: # intersected zero outward segments! # rarely occurs, the near verticals are 1/10 of a block tall for segment in verticalSegments[2:]: r = Ray.fromPoints(*segment) points.append(r.atHeight(self.sceneNode.planeHeight)) if len(points) < 4: return p1, p2, p3, p4 = points[:4] points = [p1, p2, p4, p3, p1] with gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT, GL.GL_COLOR_BUFFER_BIT): GL.glDepthMask(False) GL.glEnable(GL.GL_BLEND) GL.glVertexPointer(3, GL.GL_FLOAT, 0, numpy.array(points).ravel()) GL.glLineWidth(3.0) GL.glColor(1, 1, .1, 0.5) GL.glDisable(GL.GL_DEPTH_TEST) GL.glDrawArrays(GL.GL_LINE_STRIP, 0, len(points))
def __init__(self, dimension, textureAtlas=None, geometryCache=None, sharedGLWidget=None): """ :param dimension: :type dimension: WorldEditorDimension :param textureAtlas: :type textureAtlas: TextureAtlas :param geometryCache: :type geometryCache: GeometryCache :param sharedGLWidget: :type sharedGLWidget: QGLWidget :return: :rtype: """ QGLWidget.__init__(self, shareWidget=sharedGLWidget) self.setSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Expanding) self.setFocusPolicy(Qt.ClickFocus) self.layerToggleGroup = LayerToggleGroup() self.layerToggleGroup.layerToggled.connect(self.setLayerVisible) self.dimension = None self.worldScene = None self.loadableChunksNode = None self.textureAtlas = None self.mouseRay = Ray(Vector(0, 1, 0), Vector(0, -1, 0)) self.setMouseTracking(True) self.lastAutoUpdate = time.time() self.autoUpdateInterval = 0.5 # frequency of screen redraws in response to loaded chunks self.compassNode = self.createCompass() self.compassOrthoNode = scenegraph.OrthoNode((1, float(self.height()) / self.width())) self.compassOrthoNode.addChild(self.compassNode) self.viewActions = [] self.pressedKeys = set() self.setTextureAtlas(textureAtlas) if geometryCache is None and sharedGLWidget is not None: geometryCache = sharedGLWidget.geometryCache if geometryCache is None: geometryCache = GeometryCache() self.geometryCache = geometryCache self.matrixNode = None self.overlayNode = scenegraph.Node() self.sceneGraph = None self.renderGraph = None self.frameSamples = deque(maxlen=500) self.frameSamples.append(time.time()) self.cursorNode = None self.setDimension(dimension)
def atHeight(self, y): p1 = self.p1 p2 = self.p2 if not (p1.y < y < p2.y or p1.y > y > p2.y): return None r = Ray.fromPoints(p1, p2) return r.atHeight(y)
def drawSelf(self): if self.sceneNode.corners is None or self.sceneNode.dimension is None: return corners = self.sceneNode.corners outwardSegments = [LineSegment(corners[i], corners[j]) for i, j in self.outwardIndices] verticalSegments = [LineSegment(corners[i], corners[j]) for i, j in self.verticalIndices] points = [] for segment in outwardSegments: p = segment.atHeight(self.sceneNode.planeHeight) if p is not None: points.append(p) if len(points) < 4: # only intersected two outward segments. check the far verticals. for segment in verticalSegments[:2]: r = Ray.fromPoints(*segment) points.append(r.atHeight(self.sceneNode.planeHeight)) if len(points) < 4: # intersected zero outward segments! # rarely occurs, the near verticals are 1/10 of a block tall for segment in verticalSegments[2:]: r = Ray.fromPoints(*segment) points.append(r.atHeight(self.sceneNode.planeHeight)) if len(points) < 4: return p1, p2, p3, p4 = points[:4] points = [p1, p2, p4, p3, p1] with gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT, GL.GL_COLOR_BUFFER_BIT): GL.glDepthMask(False) GL.glEnable(GL.GL_BLEND) GL.glVertexPointer(3, GL.GL_FLOAT, 0, numpy.array(points).ravel()) GL.glLineWidth(3.0) GL.glColor(1, 1, .1, 0.5) GL.glDisable(GL.GL_DEPTH_TEST) GL.glDrawArrays(GL.GL_LINE_STRIP, 0, len(points))
def rayCastCorner(near, far): ray = Ray.fromPoints(near, far) if not any(ray.vector): return far try: #point = rayCastInBounds(ray, dimension, 50)[0] #return point or far return near + (near - far) / 4 except raycast.MaxDistanceError: return ray.atHeight(0)
def rayAtPosition(self, x, y): """ Given coordinates in screen space, return a ray in 3D space. Parameters: x and y are coordinates local to this QWidget :rtype: Ray """ p0, p1 = self.pointsAtPositions((x, y, 0.0), (x, y, 0.1)) return Ray(p0, (p1 - p0).normalize())
def currentViewMatrixChanged(self, currentView): self.viewCornersNode.corners = currentView.getViewCorners() try: targetPoint, face = rayCastInBounds(Ray(currentView.centerPoint, currentView.cameraVector), self.dimension, 100) if targetPoint is None: raise MaxDistanceError planeHeight = targetPoint.y except MaxDistanceError: planeDistance = 20 planeHeight = (currentView.centerPoint + currentView.cameraVector * planeDistance).y self.viewCornersNode.planeHeight = planeHeight
def rayCast(ray, dimension, maxDistance=100, hitAir=False): """ Borrowed from https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game Updates a factor t along each axis to compute the distance from the vector origin (in units of the vector magnitude) to the next integer value (i.e. block edge) along that axis. Return the block position and face of the block touched. Raises MaxDistanceError if the ray exceeded the max distance without hitting any blocks, or if the ray exits or doesn't enter the dimension's bounds. :param ray: :type ray: Ray :param maxDistance: :type maxDistance: int :param dimension: :type dimension: mceditlib.worldeditor.WorldEditorDimension :return: (point, face) :rtype: """ point, vector = ray if all(v == 0 for v in vector): raise ValueError("Cannot cast with zero direction ray.") bounds = dimension.bounds if point not in bounds: intersects = rayIntersectsBox(bounds, ray) if not intersects: raise RayBoundsError("Ray does not enter dimension bounds.") point = intersects[0][0] point = advanceToChunk(Ray(point, vector), dimension, maxDistance * 4) currentCX, currentCY, currentCZ = point.intfloor() currentChunk = None for pos, face in _cast(point, vector, maxDistance, 1): cx = pos[0] >> 4 cz = pos[2] >> 4 if cx != currentCX or cz != currentCZ: currentCX = cx currentCZ = cz if dimension.containsChunk(cx, cz): currentChunk = dimension.getChunk( cx, cz) # xxxx WorldEditor.recentlyLoadedChunks ID = dimension.getBlockID(*pos) if ID or hitAir: return Vector(*pos), faces.Face.fromVector(face) raise MaxDistanceError("Ray exceeded max distance.")
def rayCast(ray, dimension, maxDistance=100): """ Borrowed from https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game Updates a factor t along each axis to compute the distance from the vector origin (in units of the vector magnitude) to the next integer value (i.e. block edge) along that axis. Return the block position and face of the block touched. Raises MaxDistanceError if the ray exceeded the max distance without hitting any blocks, or if the ray exits or doesn't enter the dimension's bounds. Bypasses non-air blocks until the first air block is found, and only returns when a non-air block is found after the first air block. :param ray: :type ray: Ray :param maxDistance: :type maxDistance: int :param dimension: :type dimension: mceditlib.worldeditor.WorldEditorDimension :return: (point, face) :rtype: """ point, vector = ray if all(v == 0 for v in vector): raise ValueError("Cannot cast with zero direction ray.") bounds = dimension.bounds if point not in bounds: intersects = rayIntersectsBox(bounds, ray) if not intersects: raise RayBoundsError("Ray does not enter dimension bounds.") point = intersects[0][0] point = advanceToChunk(Ray(point, vector), dimension, maxDistance * 4) foundAir = False for pos, face in _cast(point, vector, maxDistance, 1): ID = dimension.getBlockID(*pos) if ID == 0: # xxx configurable air blocks? foundAir = True if ID and foundAir: return Vector(*pos), faces.Face.fromVector(face) if pos not in bounds: raise RayBoundsError("Ray exited dimension bounds") raise MaxDistanceError("Ray exceeded max distance.")
def getViewBounds(self): """ Get the corners of the viewing area, intersected with the world's bounds. xxx raycast to intersect with terrain height too :return: :rtype: """ corners = self.getViewCorners() # Convert the 4 corners into rays extending from the near point, then interpolate each ray at the # current dimension's height limits pairs = [] for i in range(0, 8, 2): pairs.append(corners[i:i+2]) rays = [Ray.fromPoints(p1, p2) for p1, p2 in pairs] bounds = self.dimension.bounds pointPairs = [(r.atHeight(bounds.maxy), r.atHeight(bounds.miny)) for r in rays] return sum(pointPairs, ())
if c is not None: return c def wantsChunk(self, c): return any(view.wantsChunk(c) for view in self.allViews) def recieveChunk(self, chunk): for view in self.allViews: for _ in view.recieveChunk(chunk): yield def invalidateChunk(self, (cx, cz)): for view in self.allViews: view.invalidateChunk((cx, cz)) mouseBlockPos = Vector(0, 0, 0) mouseRay = Ray(Vector(0, 1, 0), Vector(0, -1, 0)) mouseBlockFace = mceditlib.faces.FaceYIncreasing viewportMoved = QtCore.Signal(object) @property def centerPoint(self): return self.xView.centerPoint @centerPoint.setter def centerPoint(self, value): pass def subViewMoved(self, view): def _moved(): blocked = [view.blockSignals(True) for view in self.allViews]
def __init__(self, dimension, textureAtlas=None, geometryCache=None, sharedGLWidget=None): """ :param dimension: :type dimension: WorldEditorDimension :param textureAtlas: :type textureAtlas: TextureAtlas :param geometryCache: :type geometryCache: GeometryCache :param sharedGLWidget: :type sharedGLWidget: QGLWidget :return: :rtype: """ QGLWidget.__init__(self, shareWidget=sharedGLWidget) self.dimension = None self.worldScene = None self.loadableChunksNode = None self.textureAtlas = None validateWidgetQGLContext(self) self.bufferSwapDone = True if THREADED_BUFFER_SWAP: self.setAutoBufferSwap(False) self.bufferSwapThread = QtCore.QThread() self.bufferSwapper = BufferSwapper(self) self.bufferSwapper.moveToThread(self.bufferSwapThread) self.doSwapBuffers.connect(self.bufferSwapper.swap) self.bufferSwapThread.start() self.setAcceptDrops(True) self.setSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Expanding) self.setFocusPolicy(Qt.ClickFocus) self.layerToggleGroup = LayerToggleGroup() self.layerToggleGroup.layerToggled.connect(self.setLayerVisible) self.mouseRay = Ray(Vector(0, 1, 0), Vector(0, -1, 0)) self.setMouseTracking(True) self.lastAutoUpdate = time.time() self.autoUpdateInterval = 0.5 # frequency of screen redraws in response to loaded chunks self.compassNode = self.createCompass() self.compassOrtho = Ortho((1, float(self.height()) / self.width())) self.compassNode.addState(self.compassOrtho) self.viewActions = [] self.pressedKeys = set() self.setTextureAtlas(textureAtlas) if geometryCache is None and sharedGLWidget is not None: geometryCache = sharedGLWidget.geometryCache if geometryCache is None: geometryCache = GeometryCache() self.geometryCache = geometryCache self.worldNode = None self.skyNode = None self.overlayNode = scenenode.Node("WorldView Overlay") self.sceneGraph = None self.renderGraph = None self.frameSamples = deque(maxlen=500) self.frameSamples.append(time.time()) self.cursorNode = None self.setDimension(dimension)