Egg Octree

Return to Code Snippets

Postby mysticdream » Sun Nov 18, 2007 12:30 am

is it possible to explain it in easier terms?
i'm still new with panda and i dun understand what this whole code means tho it seems useful
mysticdream
 
Posts: 15
Joined: Tue Oct 09, 2007 7:36 am

Postby ditus » Fri Jan 25, 2008 1:55 pm

great :)
is the idea of the lenov diing? no!
the aries is already on build :)
User avatar
ditus
Troll
 
Posts: 277
Joined: Mon Oct 15, 2007 2:15 pm
Location: moon

Postby Icycal » Wed Feb 20, 2008 3:27 am

hello, im working on a c++ panda3d school proj and im really interested in using this oct tree to 'split' up my map model. :D

but i dont really know how to use it. do i need to complile the python code and run the python exe, or do i have to rewrite the eggoctree code into c++ and run it using c++? :roll:

im actually not too sure how the octree works, and i dont know much about using python :( it will be great if someone can give some hints :lol:
This is a Icycal-Generated post. No signature is required.
Icycal
 
Posts: 9
Joined: Wed Jan 16, 2008 1:19 am
Location: In front of my Computer

Postby ThomasEgi » Wed Feb 20, 2008 4:43 pm

example of the useage are on page 1 of this thread.

about the code. it loads an egg file. optimizes it. and saves it out again.
so basically it's yourmap.egg->optimisation->yourmap.egg
so there is no need to port it to c++ or anything. it simply modifies your model-internal structure completely outside your application.

octrees can be imagined as a cube which is divided in 8 small cubes. each of them is divided in 8 cubes again and so on and so on. the idea behind this is: get rid of all model-data you dont need right now. for collision that would mean. to check collision against 10 cubes and 5 triangles or so. instead of testing against 200000 triangles. 2000000 vs 15 checks makes a big performance difference :)
User avatar
ThomasEgi
 
Posts: 2163
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby ditus » Wed Feb 20, 2008 7:26 pm

a picture say mostly more than 1000 words ;)

Image

Image
is the idea of the lenov diing? no!
the aries is already on build :)
User avatar
ditus
Troll
 
Posts: 277
Joined: Mon Oct 15, 2007 2:15 pm
Location: moon

Postby Icycal » Wed Feb 20, 2008 9:21 pm

oh i see. thanks for the pictures, i got the idea of the octree now.

hmm i tried to do the python code (im totally new to python :lol: ), but i need some guidance on how to use the eggoctree correctly.

correct me if im wrong (i dont think im right :lol: )
this is how i did: i placed the class EggOctree into a python file called EggOctree.py, and then i created a new python file called OctreeBuilder, which i imported the EggOctree class (from EggOctree import EggOctree).

then i tried to run the 'use' code, something like this:

Code: Select all
from pandac.PandaModules import *
import direct.directbase.DirectStart
from pandac.PandaModules import CollisionTraverser,CollisionNode
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay
from pandac.PandaModules import Filename
from pandac.PandaModules import PandaNode,NodePath,Camera,TextNode
from pandac.PandaModules import Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.task.Task import Task
from direct.showbase.DirectObject import DirectObject
import random, sys, os, math
import thread

from EggOctree import EggOctree

import math

class World(DirectObject):
   def __init__(self):
      self.config()

   def config(self):
      self.np = render.attachNewNode('np')
      egg = EggData()
      egg.read(Filename('Map_Tutorial_Level.egg'))       
      egg.getFirstChild()
      sourceNode = egg.getNextChild() # the sourcenode must contain a VertexPool and a list of Polygons
      if self.np != None :
         self.np.removeNode()
         self.np = None
     
   def mainloop(self, task):

      ed = EggData()
      ed.setCoordinateSystem(egg.getCoordinateSystem())
      print 'Been Here'

      # Here, it's a quadtree since there will be only 1 leaf along the Y axis
      self.octree.build(sourceNode, ed, Vec3D(3, 100, 3))


      ed.writeEgg(Filename('Map_Tutorial_Level_octree.egg'))
      ed = None

      self.np = loader.loadModel('Map_Tutorial_Level_octree.egg')
      self.np.reparentTo(render)
      return task.cont
   
w = World()
run()


i referenced most of the stuff from roaming ralph & the octree demo, but im not sure how to apply it in python correctly, so i will need some help on this. thanks in advance :D
This is a Icycal-Generated post. No signature is required.
Icycal
 
Posts: 9
Joined: Wed Jan 16, 2008 1:19 am
Location: In front of my Computer

Postby Icycal » Fri Feb 22, 2008 12:43 am

i managed to generate a octree file, but the collision with the octree model doesnt seem to be working.

my file looks like this:
Code: Select all
<CoordinateSystem> { Z-Up }

<Collide> { Polyset keep descend }  <VertexPool> walkingground.verts {
  <Vertex> 0 {
    -24.83 -1.13694 -20.0926
    <UV> { 0.25 0 }
    <Normal> { 0 0 1 }
  }
  <Vertex> 1 {
    -24.83 -0.533698 -20.0926
    <UV> { 0.25 0.25 }
    <Normal> { 0 0 1 }
  }
  <Vertex> 2 {
    -25.2058 -0.533698 -20.0926
    <UV> { 0.234263 0.25 }
    <Normal> { 0 0 1 }
  }
  <Vertex> 3 {
    -25.2063 -1.13694 -20.0926
    <UV> { 0.234244 0 }
    <Normal> { 0 0 1 }
  }
  <Vertex> 4 {
    -14.7407 -1.13486 -20.0926
    <UV> { 0.672425 0.000859034 }
    <Normal> { 0 0 1 }
  }
  <Vertex> 5 {
    -15.4454 -1.13694 -20.0926
    <UV> { 0.642919 0 }
    <Normal> { 0 0 1 }
  }
.
.
.
.
<Group> {
  <Group> {
    <Group> {
      <Group> {
        <Group> leaf_0_0_0 {
        }
        <Group> leaf_1_0_0 {
        }
        <Group> leaf_0_0_1 {
        }
        <Group> leaf_1_0_1 {
        }
      }
      <Group> {
        <Group> leaf_2_0_0 {
        }
        <Group> leaf_3_0_0 {
        }
        <Group> leaf_2_0_1 {
        }
        <Group> leaf_3_0_1 {
        }
      }
.
.
.


does this octree file look correct?

and also, if i want to use the octree.egg in c++, do i just include the model file and do it like the normal "roaming ralph in c++" example?
please help.. thanks
This is a Icycal-Generated post. No signature is required.
Icycal
 
Posts: 9
Joined: Wed Jan 16, 2008 1:19 am
Location: In front of my Computer

tutorial request

Postby birukoff » Fri Feb 22, 2008 9:19 am

I read through this thread quite a few times and tried to code it but I still don't understand how to use this octree... :(
How to generate egg with octree? How to use it later? Could you be so kind as to modify the Roaming Ralph sample to use octree for collisions? This won't take a lot of your time I am sure, and such tutorial will be very, very helpful for those who just start using Panda and Python, like me :oops:
User avatar
birukoff
 
Posts: 424
Joined: Thu Nov 08, 2007 7:03 am
Location: Russia, Moscow

Postby ynjh_jo » Fri Feb 22, 2008 3:38 pm

Icycal :
I don't think <Collide> tag has any use outside <Group>.
You must not generated it with collide=1, did you ?
If you generated it with collide=1, the <Group>'s would have 'barrier' object type, which during loading time will be converted to collision polyset contained in collision node.

birukoff :
the author created that code only for collision, so he must strips all entries after <Comment> until the <Group> with 1 huge bulk of <VertexPool>.
So, what raytaller did with this :
Code: Select all
egg.getFirstChild()
sourceNode = egg.getNextChild() # the sourcenode must contain a VertexPool and a list of Polygons

is :
egg.getFirstChild() is used to step on the <Comment>, to reach the <Group> by using egg.getNextChild().
Yes, the children must be grabbed sequentially. To my eyes, that's the limitation by reading the .egg using EggData.
If you want to use that code, the model must be exported as a single huge geometry, then remove all entries before <Group>, except <CoordinateSystem> and <Comment>, eg. :
Code: Select all
<CoordinateSystem> { Z-Up }

<Comment> {
  "comment comment comment"
}
<Group> whatever {
   <VertexPool> vpool {
     <Vertex> 0 {
       0.024505 0.004868 -0.002914
       <UV> { 0.535007 0.506954 }
       <Normal> { 0.191266 0.0336759 0.98096 }
       <RGBA> { 1 1 1 1 }
     }
.
.
.
}

If the <VertexPool> is not inside <Group>, you must create the <Group> in order to triangulate it.


I hate that limitation, so I dropped the .egg reading using EggData. Instead, I used GeomVertexData directly to fetch the vertices and triangles.
The bonuses are :
1. I can load any format, not only .egg
2. I can select only the needed parts of the model to be processed, not the whole bulk

Addition :
1. I created adjustible cell size visualizer
2. randomly colored each resulting geom to easily distinguish them
so I only need to run the code once, and keep splitting the model until satisfied with the result.
3. added collide toggle, so when I have the desired result, I just need to toggle it and split the model once again to generate the collision mesh
The output :
1. the splitted .egg and .bam
2. (if collide=0) 2 .bam's, 1 is the randomly colored one, and 1 is white
NOTE :
it's still only for collision, I don't preserve any UV set and texture reference, only the vertex normal is preserved.
Here it is :
[EDIT] : see my next post
Last edited by ynjh_jo on Sun Feb 24, 2008 12:56 am, edited 3 times in total.
http://jon1.us
Intel Core i7-4500U | AMD Radeon M230 2GB
User avatar
ynjh_jo
 
Posts: 1795
Joined: Tue Apr 18, 2006 12:41 am
Location: Malang, Indonesia

Postby birukoff » Fri Feb 22, 2008 8:35 pm

Wow, that's great, sir! Thank you very much.

Although, I must say there is something wrong with this code. I have tried it on smaller meshes, and it works fine. But when I try to convert the bigger mesh (few hundred thousand vertices), it crashes right after generating the first file (soon after "writing egg..." appears, before "Done"), without trying to make bam files. The first generated egg seems to be corrupt also: when trying to load it into Panda, Panda crashes without any warnings...

I am going to test it with Roaming Ralph's world.egg, and let you know (it can take quite some time, since I don't understand how to use generated models for collisions while have original world.egg for rendering... Just learning...)
User avatar
birukoff
 
Posts: 424
Joined: Thu Nov 08, 2007 7:03 am
Location: Russia, Moscow

Postby ynjh_jo » Sat Feb 23, 2008 7:31 am

Yes, I've seen that case. I have a .bam terrain with 97792 vertices, 97410 tris. The result of bam2egg is even corrupted. The .egg only has half of the vertices. That terrain was built by triangles strips and flattened, without vertex normals.

Though for the other 307583 vertices, 203090 tris model, built NOT by triangles strips, with normals, it's just fine.

How did you create your model ?

[UPDATE] :
to allow me to exclude some parts of the model in the octree process, I used my SceneGraphBrowser :
http://panda3d.net/phpbb2/viewtopic.php?t=3474
Code: Select all
from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.gui.OnscreenText import OnscreenText
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import DirectLabel
from direct.showbase import PythonUtil as PU
from direct.task import Task
from random import random
import sys, math
from SceneGraphBrowser import *

def createSceneGraphBrowser():
    # create SceneGraphBrowser and point it on the model
    global SGB
    SGB = SceneGraphBrowser(
        parent=None, # where to attach SceneGraphBrowser frame
        root=sourceNodePath, # display children under this root node
        command=nodeSelected, # user defined method, executed when a node get selected,
                              # with the selected node passed to it
        contextMenu=nodeRightClicked,
        # selectTag and noSelectTag are used to filter the selectable nodes.
        # The unselectable nodes will be grayed.
        # You should use only selectTag or noSelectTag at a time. Don't use both at the same time.
        #selectTag=['select'],   # only nodes which have the tag(s) are selectable. You could use multiple tags.
        noSelectTag=['noSelect','dontSelectMe'], # only nodes which DO NOT have the tag(s) are selectable. You could use multiple tags.
        # nodes which have exclusionTag wouldn't be displayed at all
        exclusionTag=['internal component'],
        frameSize=(1,1.6),
        font=None, titleScale=.05, itemScale=.035, itemTextScale=1.2, itemTextZ=0,
        rolloverColor=(1,.8,.2,1),
        collapseAll=0, # initial tree state
        suppressMouseWheel=1,  # 1 : blocks mouse wheel events from being sent to all other objects.
                               #     You can scroll the window by putting mouse cursor
                               #     inside the scrollable window.
                               # 0 : does not block mouse wheel events from being sent to all other objects.
                               #     You can scroll the window by holding down the modifier key
                               #     (defined below) while scrolling your wheel.
        modifier='control'  # shift/control/alt
        )

selected = None
selectedIval = None
excludedText = None
excludedIval = None

def renderFrame():
    base.graphicsEngine.renderFrame()
    base.graphicsEngine.renderFrame()

def nodeSelected(np): # don't forget to receive the selected node (np)
    global selected,selectedIval,excludedText,excludedIval
    if selected!=None:
       selected.hideBounds()
       selectedIval.finish()
       selected.clearRenderMode()
    if excludedIval!=None:
       if excludedIval.isPlaying():
          excludedIval.finish()
    if np.isStashed():
       excludedText = DirectLabel(text='THIS NODE IS EXCLUDED\nright-click it again to include it back',
                    scale=.06, pad=(.5,.5), frameColor=(1,1,1,.5),
                    text_fg=(0,0,0,1),sortOrder=2000)
       excludedIval = Sequence(
                excludedText.colorScaleInterval(5,Vec4(1,1,1,0),blendType='easeIn'),
                Func(excludedText.destroy)
                )
       excludedIval.start()
       return
    selected=np
    np.showTightBounds()
    selectedIval = Sequence(
             Func(np.setRenderModeWireframe,1),
             np.colorScaleInterval(.3,Vec4(1,0,0,1)),
             np.colorScaleInterval(.3,Vec4(1,1,1,1)),
             Func(np.setRenderModeFilled,1),
             np.colorScaleInterval(.3,Vec4(1,0,0,1)),
             np.colorScaleInterval(.3,Vec4(1,1,1,1)),
             Wait(.5)
             )
    selectedIval.loop()
    print np.getName(),'SELECTED'

def nodeRightClicked(np): # don't forget to receive the selected node (np)
    if result!=None:
       return
    if np.isStashed():
       np.unstash()
    else:
       np.stash()
    updateGrids(updateSpacing=0)



class EggOctree(object):

   def build(self, sourceNodePath, destNode, cellSize, collide=False):
      sourceNode=sourceNodePath.copyTo( sourceNodePath.getParent() )
      sourceNode.flattenStrong()

      print 'storing vertices & triangles.....'
      geomList=sourceNode.findAllMatches('**/+GeomNode').asList()
      if not geomList:
         return

      vpool=EggVertexPool('octV_0')

      collNumVtx=0
      collnumface=0
      # list for the vertices
      collVertices=[]
      collUniqueVtxIdx=[]
      # list for the vertices
      collNormals=[]
      # list for the vertices index
      collFaces=[]

      # append all vertex data into a complete list
      for collGeom in geomList:
          for gIdx in range(collGeom.node().getNumGeoms()):
              currentGeom=collGeom.node().getGeom(gIdx)
              collVtxData=currentGeom.getVertexData()
              numVtx=collVtxData.getNumRows()
              #=========================================================
              # create vertex reader
              collVtxReader=GeomVertexReader(collVtxData)
              # begin reading at vertex 0
              collVtxReader.setRow(0)
              # get the vertex position column
              column=-1
              columnName=''
              while ( columnName!='vertex' ):
                    column+=1
                    collVtxReader.setColumn(column)
                    columnName=str(collVtxReader.getColumn().getName())
              #=========================================================
              # create vertex normal reader
              collNormalReader=GeomVertexReader(collVtxData)
              # begin reading at vertex 0
              collNormalReader.setRow(0)
              # get the vertex normal column
              hasNormals=1
              column=-1
              columnName=''
              while ( columnName!='normal' ):
                    column+=1
                    collNormalReader.setColumn(column)
                    if collNormalReader.getColumn()==None:
                       hasNormals=0
                       break
                    else:
                       columnName=str(collNormalReader.getColumn().getName())
              for i in range(numVtx):
                  # respect each geomNode's transform which may be exist
                  vtx=sourceNode.getRelativePoint(collGeom,collVtxReader.getData3f())
                  collVertices.append(vtx)
                  eVtx=EggVertex()
                  eVtx.setPos(Point3D(vtx[0],vtx[1],vtx[2]))
                  if hasNormals:
                     normal=sourceNode.getRelativeVector(collGeom,collNormalReader.getData3f())
                     eVtx.setNormal(Vec3D(normal[0],normal[1],normal[2]))
                  collUniqueVtxIdx.append( vpool.createUniqueVertex(eVtx).getIndex() )
              for prim in range(currentGeom.getNumPrimitives()):
                  # store the vertices index for each triangle
                  collFaceData=currentGeom.getPrimitive(prim).decompose()
                  # get the triangle counts
                  numface=collFaceData.getNumFaces()
                  for i in range(0,numface*3,3):
                      # refer to the vertex data length list created earlier
                      vtxIdx1=collNumVtx+collFaceData.getVertex(i)
                      vtxIdx2=collNumVtx+collFaceData.getVertex(i+1)
                      vtxIdx3=collNumVtx+collFaceData.getVertex(i+2)
                      vtxIdx=(collUniqueVtxIdx[vtxIdx1],collUniqueVtxIdx[vtxIdx2],collUniqueVtxIdx[vtxIdx3])
                      collFaces.append(vtxIdx)
                  collnumface+=numface
              # add the current collected vertices count
              collNumVtx+=numVtx

      collNormals=None
      print 'NUM UNIQUE VTX :',vpool.getHighestIndex()+1

#       ############################################################
#       ################ DEBUG !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#       ED=EggData()
#       ED.addChild(vpool)
#       for idx in range(collnumface):
#           # Get the triangle center
#           poly=EggPolygon()
#           for i in range(3) :
#               poly.addVertex(vpool.getVertex(collFaces[idx][i]))
#           ED.addChild(poly)
#       ED.writeEgg(Filename(output_file+'_debug.egg'))
#       return
#       ################ DEBUG !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#       ############################################################

      # model's bounding box
      minB,maxB=sourceNode.getTightBounds()
      minBB = Vec3D(minB[0]+0.001, minB[1]+0.001, minB[2]+0.001)
      maxBB = Vec3D(maxB[0]+0.001, maxB[1]+0.001, maxB[2]+0.001)

      sourceNode.removeNode()

      cellXsize=cellSize[0]
      cellYsize=cellSize[1]
      cellZsize=cellSize[2]

      # Number of leaves x,y,z
      bboxSize = maxBB - minBB
      self.ncx = math.ceil(bboxSize.getX() / cellXsize)
      self.ncy = math.ceil(bboxSize.getY() / cellYsize)
      self.ncz = math.ceil(bboxSize.getZ() / cellZsize)

      # Depth of the tree x,y,z
      self.depthX = math.ceil(math.log(self.ncx) / math.log(2))
      self.depthY = math.ceil(math.log(self.ncy) / math.log(2))
      self.depthZ = math.ceil(math.log(self.ncz) / math.log(2))
      self.depth = max(self.depthX, self.depthY, self.depthZ)

      collVertices=None

      print self.ncz, self.ncy, self.ncx
      print 'initializing cells...'
      self.cells = [[[
            {'-':
               Point3D( minBB.getX() + x * cellXsize,
                        minBB.getY() + y * cellYsize,
                        minBB.getZ() + z * cellZsize),
            '+':
               Point3D( minBB.getX() + (x+1) * cellXsize,
                        minBB.getY() + (y+1) * cellYsize,
                        minBB.getZ() + (z+1) * cellZsize),
            'g':EggGroup('L_%d_%d_%d' % (x,y,z))}

            for z in range(self.ncz)]
            for y in range(self.ncy)]
            for x in range(self.ncx)]

      print 'Cell grid is %dx%dx%d' % (len(self.cells), len(self.cells[0]), len(self.cells[0][0]))

      if collide :
         for x in range(self.ncx) :
            for y in range(self.ncy) :
               for z in range(self.ncz) :
                  self.cells[x][y][z]['g'].addObjectType('barrier')

      print 'filling cells...'
      # Iterate on the triangles
      for idx in range(collnumface):
          # Get the triangle center
          polyCenter = Point3D(0,0,0)
          poly=EggPolygon()
          trie=collFaces[idx]
          for i in range(3) :
              vtx=vpool.getVertex( trie[i] )
              polyCenter +=vtx.getPos3()
              poly.addVertex(vtx)
          polyCenter /= 3.0
          # Add the triangle to the corresponding cell group
          cx = int(math.floor((polyCenter[0]-minBB[0]) / cellXsize))
          cy = int(math.floor((polyCenter[1]-minBB[1]) / cellYsize))
          cz = int(math.floor((polyCenter[2]-minBB[2]) / cellZsize))

          # doing this would yield uniform normals for all 3 vertices
          # so, preserve normals from the vertex data instead
          # (already done above after creating EggVertex)
#           if not hasNormals:
#              poly.recomputePolygonNormal()
          self.cells[cx][cy][cz]['g'].addChild(poly)

      # Add the vertex data
      destNode.addChild(vpool)

      print 'Started........'
      self.nleaves = self.recur(destNode, 0, 0,0,0)
      print self.nleaves, 'leaves added'

   def recur(self, node, depth, x, y, z) :
      if depth < self.depth :
         nnode = EggGroup('')
         delt = int(math.pow(2, self.depth - depth - 1))

         nchilds = 0

         nchilds += self.recur(nnode, depth+1, x, y, z)
         nchilds += self.recur(nnode, depth+1, x + delt, y, z)
         nchilds += self.recur(nnode, depth+1, x, y + delt, z)
         nchilds += self.recur(nnode, depth+1, x + delt, y + delt, z)

         nchilds += self.recur(nnode, depth+1, x, y, z + delt)
         nchilds += self.recur(nnode, depth+1, x + delt, y, z + delt)
         nchilds += self.recur(nnode, depth+1, x, y + delt, z + delt)
         nchilds += self.recur(nnode, depth+1, x + delt, y + delt, z + delt)

         if nchilds > 0 :
            node.addChild(nnode)
         return nchilds

      else :

         # We are in a leaf
         if x < self.ncx and y < self.ncy and z < self.ncz :
            node.addChild(self.cells[x][y][z]['g'])
            return 1
         else:
            return 0

eoct=EggOctree()

distX=4.
distY=4.
distZ=4.
result=None
inc=.1
adjusted=-1

grid=render.attachNewNode('grid')
grid.setLightOff(1)

LSgrid=LineSegs()
LSgrid.setThickness(1)

base.setBackgroundColor(.3,.7,1,1)
base.camLens.setNearFar(.1,100000)
loadingText=OnscreenText(text='LOADING.....',scale=.12,fg=(1,0,0,1),shadow=(0,0,0,1))
renderFrame()
loadingText.destroy()

offOnText=['OFF','ON']
offOnFG=[(1,0,0,1),(0,0,1,1)]
walkerTextParent=aspect2d.attachNewNode('')
walkerTextParent.setZ(-1.2)
walkerText = OnscreenText( parent=walkerTextParent,
        text='WALKING ON SCENEGRAPH HIERARCHY',pos=(0,.075),scale=.05,mayChange=1)
stepOnText = OnscreenText( parent=walkerTextParent,
        text='sdf',pos=(0,.02),scale=.05,mayChange=1)
gridPropsTextParent=aspect2d.attachNewNode('')
gridPropsTextParent.setZ(-1)
incText = OnscreenText( parent=gridPropsTextParent,
        text='increment : %.3f'%inc,pos=(0,.02),scale=.05,mayChange=1)
XYZtext=[]
XYZtext.append( OnscreenText( parent=gridPropsTextParent,
                text='X : %.3f'%distX,pos=(-.5,.075),scale=.05,mayChange=1) )
XYZtext.append( OnscreenText( parent=gridPropsTextParent,
                text='Y : %.3f'%distY,pos=(  0,.075),scale=.05,mayChange=1) )
XYZtext.append( OnscreenText( parent=gridPropsTextParent,
                text='Z : %.3f'%distZ,pos=( .5,.075),scale=.05,mayChange=1) )

def setupLights():
    ambientLight = AmbientLight( 'ambientLight' )
    ambientLight.setColor( Vec4(0.3, 0.3, 0.3, 1) )

    directionalLight = DirectionalLight( 'directionalLight1' )
    directionalLight.setDirection( Vec3( 1, 2,-1 ) )
    directionalLight.setColor( Vec4( .7, .7, .7, 1 ) )

    render.setLight(render.attachNewNode( ambientLight ))
    render.setLight(render.attachNewNode( directionalLight ))

def updateGrids(g=(1,1,1),inc=0,updateSpacing=1):
    global distX,distY,distZ
    TB1,TB2=sourceNodePath.getTightBounds()
    TB=TB2-TB1
    threshold=.1
    if updateSpacing:
       if g[0] and TB[0]>threshold:
          distX=PU.clampScalar(threshold,TB[0],distX+inc)
          XYZtext[0]['text']='X : %.3f'%distX
       if g[1] and TB[1]>threshold:
          distY=PU.clampScalar(threshold,TB[1],distY+inc)
          XYZtext[1]['text']='Y : %.3f'%distY
       if g[2] and TB[2]>threshold:
          distZ=PU.clampScalar(threshold,TB[2],distZ+inc)
          XYZtext[2]['text']='Z : %.3f'%distZ
    # remove all grids ###############################################
    for g in grid.findAllMatches('**/+GeomNode').asList():
        g.removeNode()
    # gridX ###############################################
    LSgrid.reset()
    LSgrid.setColor(1,1,0)
    for z in range(int(1+TB[2]/distZ)):
        for x in range(int(1+TB[0]/distX)):
            LSgrid.moveTo(x*distX,0,z*distZ)
            LSgrid.drawTo(x*distX,TB[1],z*distZ)
        LSgrid.moveTo(TB[0],0,z*distZ)
        LSgrid.drawTo(TB[0],TB[1],z*distZ)
    for x in range(int(1+TB[0]/distX)):
        LSgrid.moveTo(x*distX,0,TB[2])
        LSgrid.drawTo(x*distX,TB[1],TB[2])
    LSgrid.moveTo(TB[0],0,TB[2])
    LSgrid.drawTo(TB[0],TB[1],TB[2])
    grid.attachNewNode(LSgrid.create()).setPos(TB1)
    # gridY ###############################################
    LSgrid.reset()
    LSgrid.setColor(1,0,0)
    for z in range(int(1+TB[2]/distZ)):
        for y in range(int(1+TB[1]/distY)):
            LSgrid.moveTo(0,y*distY,z*distZ)
            LSgrid.drawTo(TB[0],y*distY,z*distZ)
        LSgrid.moveTo(0,TB[1],z*distZ)
        LSgrid.drawTo(TB[0],TB[1],z*distZ)
    for y in range(int(1+TB[1]/distY)):
        LSgrid.moveTo(0,y*distY,TB[2])
        LSgrid.drawTo(TB[0],y*distY,TB[2])
    LSgrid.moveTo(0,TB[1],TB[2])
    LSgrid.drawTo(TB[0],TB[1],TB[2])
    grid.attachNewNode(LSgrid.create()).setPos(TB1)
    # gridZ ###############################################
    LSgrid.reset()
    LSgrid.setColor(0,0,1)
    for x in range(int(1+TB[0]/distX)):
        for y in range(int(1+TB[1]/distY)):
            LSgrid.moveTo(x*distX,y*distY,0)
            LSgrid.drawTo(x*distX,y*distY,TB[2])
        LSgrid.moveTo(x*distX,TB[1],0)
        LSgrid.drawTo(x*distX,TB[1],TB[2])
    for y in range(int(1+TB[1]/distY)):
        LSgrid.moveTo(TB[0],y*distY,0)
        LSgrid.drawTo(TB[0],y*distY,TB[2])
    LSgrid.moveTo(TB[0],TB[1],0)
    LSgrid.drawTo(TB[0],TB[1],TB[2])
    grid.attachNewNode(LSgrid.create()).setPos(TB1)

def adjustX():
    global adjusted
    adjusted=0
    updateXYZtextScale()

def adjustY():
    global adjusted
    adjusted=1
    updateXYZtextScale()

def adjustZ():
    global adjusted
    adjusted=2
    updateXYZtextScale()

def updateXYZtextScale():
    for t in XYZtext:
        t['scale']=.05
        t['fg']=(0,0,0,1)
    XYZtext[adjusted]['scale']=.07
    XYZtext[adjusted]['fg']=(0,0,1,1)

def adjustGrid(dir):
    if adjusted==-1:
       print "CHOOSE X, Y, or Z"
       return
    adjustedGrid=[0,0,0]
    adjustedGrid[adjusted]=1
    if grid.isHidden():
       grid.show()
    updateGrids(adjustedGrid,dir*inc)

def adjustInc(Iinc):
    global inc
    inc=PU.clampScalar(.05,100,inc+Iinc)
    incText['text']='increment : %.3f'%inc

def toggleCollision():
    global collide
    collide=not collide
    collText['text']='COLLISION : '+offOnText[collide]
    collText['fg']=offOnFG[collide]

def toggleGridVis():
    if grid.isHidden():
       grid.show()
    else:
       grid.hide()

def toggleHierarchyWalk(status=None):
    global result,isWalkingOnHierarchy,selected
    if status==None:
       isWalkingOnHierarchy=not isWalkingOnHierarchy
    else:
       isWalkingOnHierarchy=status
    if isWalkingOnHierarchy:
       setMode(MODE_hierarchyWalk)
       if selected==None:
          if result==None:
             SGB.command(sourceNodePath)
             stepOnText['text']=sourceNodePath.getName()
          else:
             SGB.command(result)
             stepOnText['text']=result.getName()
       gridPropsTextParent.posInterval(.25,Point3(0,0,-1.2),blendType='easeIn').start()
       walkerTextParent.posInterval(.25,Point3(0,0,-1),blendType='easeIn').start()
       text=helpWalker+helpAllModes
       if result==None:
          text+=helpInclusion
       helpText['text']=text
    else:
       setMode(MODE_gridAdjustment)
       gridPropsTextParent.posInterval(.25,Point3(0,0,-1),blendType='easeIn').start()
       walkerTextParent.posInterval(.25,Point3(0,0,-1.2),blendType='easeIn').start()
       text=helpGrid+helpAllModes
       if result==None:
          text+=helpInclusion
       helpText['text']=text
   
def getChildIndex(np):
    children=np.getParent().getChildrenAsList()
    for c in children:
        if c==np:
           return children.index(np)

def gotoParent():
    if selected!=sourceNodePath and selected!=result:
       parent=selected.getParent()
       SGB.selectNode(parent)
       numChildren=parent.getParent().getNumChildren()
       selfIdx=getChildIndex(selected)
       if parent!=sourceNodePath and parent!=result:
          stepOnText['text']='child #%s of %s (num children : %s)'%(selfIdx+1,numChildren,parent.getNumChildren())

def gotoChild():
    numChildren=selected.getNumChildren()
    if numChildren:
       SGB.selectNode(selected.getChild(0))
       stepOnText['text']='child #%s of %s (num children : %s)'%(1,numChildren,selected.getNumChildren())

def gotoNextSibling():
    if selected==sourceNodePath or selected==result:
       return
    parent=selected.getParent()
    siblingIdx=getChildIndex(selected)+1
    if siblingIdx<parent.getNumChildren():
       SGB.selectNode(parent.getChild(siblingIdx))
       stepOnText['text']='child #%s of %s (num children : %s)'%(siblingIdx+1,parent.getNumChildren(),selected.getNumChildren())

def gotoPrevSibling():
    if selected==sourceNodePath or selected==result:
       return
    parent=selected.getParent()
    siblingIdx=getChildIndex(selected)-1
    if siblingIdx>-1:
       SGB.selectNode(parent.getChild(siblingIdx))
       stepOnText['text']='child #%s of %s (num children : %s)'%(siblingIdx+1,parent.getNumChildren(),selected.getNumChildren())

def setMode(mode):
    base.buttonThrowers[0].node().setPrefix(mode+':-> ')

def acceptKey(event,method,extraArgs=[]):
    for mode in MODES:
        DO.accept('%s:-> %s'%(mode,event),method,extraArgs)
       
def zoomNode(toNode=0):
    if toNode:
       if selected==None:
          return
       else:
          np=selected
    else:
       np=sourceNodePath
    if np.getBounds().isEmpty():
       return
    center=np.getBounds().getCenter()
    radius=np.getBounds().getRadius()

    camPivot.setPos(center)
    camPivot.setHpr(camera,0,0,0)
    camera.setPos(0,-radius*5,0)
    camera.lookAt(0,0,0)
    mat4=Mat4(camera.getMat())
    mat4.invertInPlace()
    base.mouseInterfaceNode.setMat(mat4)

def reviveModel():
    global result,selected
    if result==None:
       return
    if selected!=None:
       selected.hideBounds()
       selectedIval.finish()
       selected.clearRenderMode()
       selected=None
    loader.unloadModel(result)
    result.removeNode()
    result=None

    # revive the model
    sourceNodePath.unstash()
    # put a notification text
    waitText = DirectLabel(text='updating SceneGraphBrowser.....',
             scale=.045,text_fg=(0,0,0,1), frameColor=(1,1,1,.8), pad=(.5,.5),
             sortOrder=2000)
    renderFrame()
    waitText.destroy()
    # restore stashed nodes, so it will be found by the SGBrowser
    stashedNPs=sourceNodePath.findAllMatches('**/@@*')
    stashedNPs.unstash()
    # display the model's scenegraph
    SGB.setRoot(sourceNodePath)
    # stash them back
    stashedNPs.stash()
    stashedNPs.clear()
    toggleHierarchyWalk(0)
    print 'REVIVED !!!'

def splitModel():
    global result,selected
    waitText = DirectLabel(text='please wait.....',
             scale=.1, frameColor=(1,1,1,.7), pad=(1,.2), text_fg=(0,0,0,1),
             sortOrder=2000)
    renderFrame()
    waitText.destroy()

    ed = EggData()
    ed.setCoordinateSystem(0) # set it to Z-Up
    eoct.build(sourceNodePath, ed, Vec3D(distX, distY, distZ), collide=collide)
    print 'writing EGG...',
    if collide:
       ed.writeEgg(Filename(output_file+'_coll.egg'))
    else:
       ed.writeEgg(Filename(output_file+'.egg'))
    print 'done'

    if selected!=None:
       selected.hideBounds()
       selectedIval.finish()
       selected.clearRenderMode()
       selected=None
    sourceNodePath.stash()
    if result!=None:
       loader.unloadModel(result)
       result.removeNode()

    result = NodePath( loadEggData(ed) )
    result.setName(output_file)
    result.reparentTo(render)
    if collide:
       result.writeBamFile(output_file+'_coll.bam')
       result.setTransparency(0,10)
       for g in result.findAllMatches('**/+CollisionNode').asList():
           g.show()
    else:
       result.writeBamFile(output_file+'_White.bam')
       for g in result.findAllMatches('**/+GeomNode').asList():
           g.setColor(random(),random(),random(),1)
       result.writeBamFile(output_file+'_Colored.bam')

    waitText = DirectLabel(text='updating SceneGraphBrowser.....',
             scale=.045,text_fg=(0,0,0,1), frameColor=(1,1,1,.8), pad=(.5,.5),
             sortOrder=2000)
    renderFrame()
    waitText.destroy()
    SGB.setRoot(result)
#     result.ls()
    doneText = DirectLabel(text='DONE',
             scale=.1, frameColor=(1,1,1,.7), pad=(1,.2),text_fg=(0,0,0,1),
             sortOrder=2000)
    Sequence(
             doneText.colorScaleInterval(3,Vec4(1,1,1,.2)),
             Func(doneText.destroy)
             ).start()

    print '###########################################'
    print '###########################################'
    result.analyze()
    print '###########################################'
    print '###########################################'
    toggleHierarchyWalk(0)
    ed = None

DO=DirectObject()
MODE_hierarchyWalk='[walking around]'
MODE_gridAdjustment='[adjusting grid]'
isWalkingOnHierarchy=0

MODES=[MODE_gridAdjustment]
acceptKey('x',adjustX)
acceptKey('y',adjustY)
acceptKey('z',adjustZ)
acceptKey('arrow_right',adjustInc,[.05])
acceptKey('arrow_right-repeat',adjustInc,[.05])
acceptKey('arrow_left',adjustInc,[-.05])
acceptKey('arrow_left-repeat',adjustInc,[-.05])
acceptKey('arrow_up',adjustGrid,[1])
acceptKey('arrow_up-repeat',adjustGrid,[1])
acceptKey('arrow_down',adjustGrid,[-1])
acceptKey('arrow_down-repeat',adjustGrid,[-1])

MODES=[MODE_hierarchyWalk]
acceptKey('arrow_right',gotoNextSibling)
acceptKey('arrow_left',gotoPrevSibling)
acceptKey('arrow_up',gotoParent)
acceptKey('arrow_down',gotoChild)

MODES=[MODE_gridAdjustment,MODE_hierarchyWalk]
acceptKey('escape',sys.exit)
acceptKey('enter',splitModel)
acceptKey('r',reviveModel)
acceptKey('c',toggleCollision)
acceptKey('w',base.toggleWireframe)
acceptKey('g',toggleGridVis)
acceptKey('h',toggleHierarchyWalk)
acceptKey('s',base.screenshot)
acceptKey('a',zoomNode,[0])
acceptKey('f',zoomNode,[1])

helpGrid='''[ X/Y/Z ] : select X/Y/Z axis
[ arrow up/down ] : adjust cell size along selected axis
[ arrow left/right ] : adjust cell size increment

'''
helpWalker='''[ arrow up/down ] : go up to parent or down to children
[ arrow left/right ] : go to previous/next sibling

'''
helpAllModes='''[ ENTER ] : SPLIT NOW !!
[ R ] : revive model
[ C ] : toggle collision
[ W ] : toggle wireframe
[ G ] : toggle grid visibility
[ H ] : toggle grid adjustment / hierarchy walker
[ SPACE ] : toggle SceneGraphBrowser
[ A ] : zoom extent
[ F ] : zoom selected node'''
helpInclusion='''

RIGHT CLICK node in SceneGraphBrowser :
    toggle inclusion in octree process'''
helpText = OnscreenText(text=helpGrid+helpAllModes+helpInclusion,
         scale=.045,align=TextNode.ALeft,fg=(0,0,0,1),mayChange=1)
NodePath(helpText).setPos(render2d,-.995,0,.95)

setupLights()

##########################################################
##  THE INPUT FILE CAN BE ANY OF THE SUPPORTED FORMATS  ##
##########################################################
input_file='teapot.egg'#    <----- YOUR FILE
# input_file='panda.egg'#    <----- YOUR FILE
# input_file='RalphWorld.egg'#    <----- YOUR FILE
output_file=input_file[:input_file.rfind('.')]+'-split'
#=========================================================
collide=0
##########################################################
##########################################################

collText=OnscreenText(text='COLLISION : '+offOnText[collide],
         scale=.05,pos=(0,-.86),fg=offOnFG[collide],shadow=(0,0,0,1), mayChange=1)

sourceNodePath=loader.loadModel(input_file)
sourceNodePath.reparentTo(render)
sourceNodePath.analyze()

camPivot=render.attachNewNode('camPivot')
camera.reparentTo(camPivot)
camera.setPos(1,-1.2,1)
camera.lookAt(0,0,0)
zoomNode(0)
updateGrids()
waitText = DirectLabel(text='creating SceneGraphBrowser.....',
         scale=.045,text_fg=(0,0,0,1), frameColor=(1,1,1,.9), pad=(.5,.5),
         sortOrder=2000)
renderFrame()
waitText.destroy()

createSceneGraphBrowser()
acceptKey('space',SGB.toggleVisibility)
setMode(MODE_gridAdjustment)
run()

Image
and don't call me sir, I'm not bearded yet. :lol:
Last edited by ynjh_jo on Sun Feb 24, 2008 11:44 am, edited 3 times in total.
http://jon1.us
Intel Core i7-4500U | AMD Radeon M230 2GB
User avatar
ynjh_jo
 
Posts: 1795
Joined: Tue Apr 18, 2006 12:41 am
Location: Malang, Indonesia

Postby birukoff » Sat Feb 23, 2008 9:50 am

My egg is modeled in Blender and exported with Chicken. I loaded it into Panda without problems (except it took quite a while to load).

I tried to convert it with your latest code but it was not possible, it doesn't even load there. There is a warning in console: "Assertion failed: _current_index + sizeof(tempvar) <= _datagram->get_length() at line 277 of c:\temp\mkpr\built\include\datagramIterator.I"

With smaller terrain from Roaming Ralph it works fine.
User avatar
birukoff
 
Posts: 424
Joined: Thu Nov 08, 2007 7:03 am
Location: Russia, Moscow

Postby ynjh_jo » Sat Feb 23, 2008 12:48 pm

Honestly I don't know what's going on there. My problem here is even more weird.
It's my 97792 vertices, 97410 tris model, which is a single geom, a single primitive. After collecting the vertices & triangles, I stored the raw result to an .egg, but it couldn't be loaded.
I've hunted it down and came up with this piece :
http://ynjh.panda3dprojects.com/debug_egg.zip
It's the first 61448 triangles, but can't be loaded. I doubt that 61448 is the triangle counts limit, but if I remove any of the <Polygon>, it's loaded.
Does anyone know what's wrong ?
http://jon1.us
Intel Core i7-4500U | AMD Radeon M230 2GB
User avatar
ynjh_jo
 
Posts: 1795
Joined: Tue Apr 18, 2006 12:41 am
Location: Malang, Indonesia

Postby Icycal » Sat Feb 23, 2008 12:52 pm

hmm.. my model has 50k poly count. does that mean its one of the reasons it cant be loaded as well?

till now i still cant figure out how to actually use the eggOctree lolx. but i found some ways to optimize my map and solved my ctrav-affected problem. good luck on ur problems guys :)
This is a Icycal-Generated post. No signature is required.
Icycal
 
Posts: 9
Joined: Wed Jan 16, 2008 1:19 am
Location: In front of my Computer

Postby ynjh_jo » Sun Feb 24, 2008 11:43 am

[UPDATE] :
1. I don't use this original part anymore :
Code: Select all
      # Loop on the vertices determine the bounding box
      minBB = Point3D()
      maxBB = Point3D()

      nverts = vpool.getHighestIndex()
      for iv in range(nverts + 1) :
         vtx = vpool[iv]
         vtxPos = vtx.getPos3()

         # Sets the X, Y or Z components
         i = 0
         for set in [Point3D.setX, Point3D.setY, Point3D.setZ]  :
            if vtxPos[i] < minBB[i] :
               set(minBB, vtxPos[i])

            if vtxPos[i] > maxBB[i] :
               set(maxBB, vtxPos[i])
            i += 1

      minBB -= Vec3D(0.001, 0.001, 0.001)
      maxBB += Vec3D(0.001, 0.001, 0.001)

There was a little noticable offset in the result, along Z. Now I use tight bounds, which gives acurate result :
Code: Select all
      # model's bounding box
      minB,maxB=sourceNode.getTightBounds()
      minBB = Vec3D(minB[0]+0.001, minB[1]+0.001, minB[2]+0.001)
      maxBB = Vec3D(maxB[0]+0.001, maxB[1]+0.001, maxB[2]+0.001)

2. improved vertices addition to the pool, now vertices are unique to avoid duplicated vertices.
3. added scenegraph hierarchy walk, just like in pview, using arrow keys, in addition to the GUI.
4. fixed 1 tiny bug in the SceneGraphBrowser GUI.
http://panda3d.net/phpbb2/viewtopic.php?t=3474

The result of this octree is a single huge vertex pool and the octree groups of geoms. I'm still clueless about the failure involving poly counts. It failed on my 97792 vertices, 97410 tris model, but not on my 307583 vertices, 203090 tris model. It's silent death, no spit on the console at all. I checked for maximum poly count for each octree'd geom, it isn't excessive at all. Each geom's poly counts is limited by the cell size, so using a small cell size should not fail, but it failed. So, it's definitely something else beyond my reach :S
If someone could help, please H E L P !!
Thank you.
http://jon1.us
Intel Core i7-4500U | AMD Radeon M230 2GB
User avatar
ynjh_jo
 
Posts: 1795
Joined: Tue Apr 18, 2006 12:41 am
Location: Malang, Indonesia

Postby treeform » Mon May 26, 2008 9:11 pm

I had some problem with the egg script.

1. It used fix chunk cells ( so the polygons would not get divided as well when there is high density)
2. It left lots of empty collision nodes in the egg
3. And most importantly guessing the cell size is hard! I have models in meters and never know weather its 1, .1 or 100... that was a big hindrance!

This script is a little faster too and provides command line interface. Try it out!

Some ideas for future implementation:
Limit the number of the smallest cell - why divide further if you will never fit through the hole.
Write the collision system back into the source egg.
Porting it to c++ (why bother its already super fast)

Code: Select all
#!/usr/bin/python
"""
                             _                 
                            | |               
  ___  __ _  __ _  ___   ___| |_ _ __ ___  ___
 / _ \/ _` |/ _` |/ _ \ / __| __| '__/ _ \/ _ \
|  __/ (_| | (_| | (_) | (__| |_| | |  __/  __/
 \___|\__, |\__, |\___/ \___|\__|_|  \___|\___|
       __/ | __/ | by treeform                           
      |___/ |___/                                                             
                             
This is a replacement of raytaller wonderful
Egg Octree script many people had problem using it
( i always guessed wrong about the size of cells )
and it generated many "empty" branches which this
one does not. 
original see : ( http://panda3d.org/phpbb2/viewtopic.php?t=2502 )
This script like the original also released under the WTFPL license.
Usage: egg-octreefy [args] [-o outfile.egg] infile.egg [infile.egg...] 
-h     display this
-v     verbose
-l     list resulting egg file
-n     number of triangles per leaf (default 3)
if outfile is not specified "infile"-octree.egg assumed
"""
import sys, getopt
import math
from pandac.PandaModules import *
global verbose,listResultingEgg,maxNumber
listResultingEgg = False
verbose = False
maxNumber = 3
   
def getCenter(vertexList):
    """ get a list of Polywraps and figure out their center """
    # Loop on the vertices determine the bounding box
    center = Point3D(0,0,0)
    i = False
    for vtx in vertexList:
        center += vtx.center
        i+=1
    if i:
        center /= i
    return center

def flatten(thing):
    """ get nested tuple structure like quadrents and flatten it """
    if type(thing) == tuple:
        for element in thing:
            for thing in flatten(element):
                yield thing
    else:
        yield thing

def splitIntoQuadrants(vertexList,center):
    """
        +---+---+    +---+---+
        | 1 | 2 |    | 5 | 6 |
        +---+---+    +---+---+
        | 3 | 4 |    | 7 | 8 |
        +---+---+    +---+---+
        put all poly wraps into quadrents
    """
    quadrants = ((([],[]),
                 ([],[])),
                 (([],[]),
                  ([],[])))
    for vtx in vertexList:
        vtxPos = vtx.center
        x =  vtxPos[0] > center[0]
        y =  vtxPos[1] > center[1]
        z =  vtxPos[2] > center[2]
        quadrants[x][y][z].append(vtx)
    quadrants = flatten(quadrants)
    return quadrants

class Polywrap:
    """
        its a class that defines polygons center
        so that it does not have to be recomputed
    """
    polygon = None
    center = None
   
    def __str__(self):
        """ some visualization to aid debugging """
        return str(self.polygon.getNumVertices())+":"+str(self.center)
   
def genPolyWraps(group):
    """ generate a list of polywraps form a group of polygons """
    for polygon in iterChildren(group):
        if type(polygon) == EggPolygon:
            center = Vec3D()
            i = False
            for vtx in iterVertexes(polygon):
                center += vtx.getPos3()
                i += 1
            if i : center /= i
            pw = Polywrap()
            pw.polygon = polygon
            pw.center = center
            yield pw
         
def buildOctree(group):
    """ build an octree form a egg group """
    global verbose
    group.triangulatePolygons(0xff)
    polywraps = [i for i in genPolyWraps(group)]
    if verbose: print len(polywraps),"triangles"
    center = getCenter(polywraps)
    quadrants = splitIntoQuadrants(polywraps,center)
    eg = EggGroup('octree-root')
    for node in recr(quadrants):
        eg.addChild(node)
    return eg

def recr(quadrants,indent=0):
    """
        visit each quadrent and create octree there
        all the end consolidate all octrees into egg groups
    """
    global verbose,maxNumber
    qs = [i for i in quadrants]
    if verbose: print "    "*indent,"8 quadrents have ",[len(i) for i in qs]," triangles"
    for quadrent in qs:
        if len(quadrent) == 0:
            if verbose: print "    "*indent," no triangles at this quadrent"
            continue
        elif len(quadrent) <= maxNumber:
            center = getCenter(quadrent)
            if verbose: print "    "*indent," triangle center", center, len(quadrent)
            eg = EggGroup('leaf %i tri'%len(quadrent))
            eg.addObjectType('barrier')
            for pw in quadrent:
                eg.addChild(pw.polygon)
                if eg.getFirstChild : yield eg
        else:
            eg = EggGroup('branch-%i'%indent)
            center = getCenter(quadrent)
            for node in recr(splitIntoQuadrants(quadrent,center),indent+1):
                eg.addChild(node)
            if eg.getFirstChild : yield eg
     
def iterChildren(eggNode):
    """ iterate all children of a node """
    try:
        child = eggNode.getFirstChild()
        while child:
            yield child
            child = eggNode.getNextChild()
    except:
        pass
   
def iterVertexes(eggNode):
    """ iterate all vertexes of polygon or polylist """
    try:
        index = eggNode.getHighestIndex()
        for i in xrange(index+1):
            yield eggNode.getVertex(i)
    except:
        index = eggNode.getNumVertices()
        for i in xrange(index):
            yield eggNode.getVertex(i)
        pass

def eggLs(eggNode,indent=0):
    """ list whats in our egg """
    if eggNode.__class__.__name__ != "EggPolygon":
        print " "*indent+eggNode.__class__.__name__+" "+eggNode.getName()
        for eggChildren in iterChildren(eggNode):
            eggLs(eggChildren,indent+1)
       
def eggStripTexture(eggNode):
    """ strip textures and materials """
    if eggNode.__class__ == EggPolygon:
        eggNode.clearTexture()
        eggNode.clearMaterial()       
    else:
        for eggChildren in iterChildren(eggNode):
            eggStripTexture(eggChildren)
           
           
def octreefy(infile,outfile):
    """
        octreefy infile and write to outfile
        using the buildOctree functions
    """
    egg = EggData()
    egg.read(Filename(infile))
    eggStripTexture(egg)
    group = egg
    vertexPool = False
    # find the fist group and fine the first vertexPool
    # you might have to mess with this if your egg files
    # are in odd format
    for child in iterChildren(egg):
        if type(child) == EggVertexPool:
            vertexPool = child
        if type(child) == EggGroup:
            group = child
    # if we have not found the vertexPool it must be inside
    if not vertexPool:
        for child in iterChildren(group):
            if type(child) == EggVertexPool:
                vertexPool = child
    if vertexPool and group:
        ed = EggData()
        ed.setCoordinateSystem(egg.getCoordinateSystem())
        ed.addChild(vertexPool)
        ed.addChild(buildOctree(group))
        if listResultingEgg: eggLs(ed)
        ed.writeEgg(Filename(outfile))
       
def main():
    """ interface to our egg octreefier """
    try:
        optlist, list = getopt.getopt(sys.argv[1:], 'hlvo:n:')
    except Exception,e:
        print e
        sys.exit(0)
    global verbose,listResultingEgg,maxNumber
    outfile = False
    for opt in optlist:
        if opt[0] == '-h':
            print __doc__
            sys.exit(0)
        if opt[0] == '-l':
            listResultingEgg = True
        if opt[0] == '-v':
            verbose = True
        if opt[0] == '-n':
            maxNumber = int(opt[1])
        if opt[0] == '-o':
            outfile = opt[1]
    if outfile and len(list) > 1:
        print "error can have an outfile and more then one infile"
        sys.exit(0)
       
    for file in list:
        if '.egg' in file:
            if verbose: print "processing",file
            if outfile:
                octreefy(file,outfile)
            else:
                octreefy(file,file.replace(".egg","-octree.egg"))
                 
if __name__ == "__main__":
    import os
    main()
User avatar
treeform
 
Posts: 2106
Joined: Sat May 05, 2007 5:15 pm
Location: SF, CA

Postby ThomasEgi » Sat Sep 20, 2008 2:56 pm

hey treeform,
i desperatly tried to get sane results with your egg octee script.
for some models it produced output files, for some it didnt. and the files it produced did not show a thing when opened with pview or loaded elsewhise. contained some vertexpoll and groups but nothing that would draw at all.
i tried a whole bunch of models, blender chicken exports, x2egg, models with textures, and without.
it does not throw any errors or anything.
any chance you can fix it up so its working(again)?
User avatar
ThomasEgi
 
Posts: 2163
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby ynjh_jo » Sun Sep 21, 2008 1:40 am

I haven't tried treeform's.
Does shift-c in pview help, Thomas ?
http://jon1.us
Intel Core i7-4500U | AMD Radeon M230 2GB
User avatar
ynjh_jo
 
Posts: 1795
Joined: Tue Apr 18, 2006 12:41 am
Location: Malang, Indonesia

Postby ThomasEgi » Sun Sep 21, 2008 7:38 am

shift-c shows the mesh. but it has many holes, especially smaller triangles are missing. many of them.
some models however wont get converted at all. it doesnt even create an outputfile.
i already asked treeform on irc to have a look at it, and if possible make it work for visible geometry,too. since octrees can be a major speedup and allows generic map optimisation like ->flatenStrong->octree. which is usefull in many cases.
User avatar
ThomasEgi
 
Posts: 2163
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby treeform » Sun Sep 21, 2008 1:34 pm

could you provide the models you have problems with?

If there is no error at all most likely it seg faulted ... so i would need your models to track that.

I ran some tests on it no holes or stuff found ...
User avatar
treeform
 
Posts: 2106
Joined: Sat May 05, 2007 5:15 pm
Location: SF, CA

Postby ThomasEgi » Sun Sep 21, 2008 1:52 pm

http://home.arcor.de/positiveelectron/files/octree-models.zip
one has holes, the other does not produce any output at all
User avatar
ThomasEgi
 
Posts: 2163
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby adr » Tue Mar 10, 2009 4:54 pm

Hey ^^:, I can't for the life of me understand how to get a working model to work. How do you get this to work? Don't point me to the 1st post, cuase I don't get how to get it to from that lol.
adr
 
Posts: 532
Joined: Wed Oct 12, 2005 8:26 pm
Location: USA

Postby treeform » Tue Mar 10, 2009 5:11 pm

if you use my script:
Code: Select all
python egg-octreefy.py -o level-collision.egg level-visual.egg


Then you just load one file as visual object and the other as the collision object.
User avatar
treeform
 
Posts: 2106
Joined: Sat May 05, 2007 5:15 pm
Location: SF, CA

Postby adr » Tue Mar 10, 2009 6:43 pm

Cool it works. After a little testing I fine it only works on one object with in a .egg. Is there any way to for "loop" it to do all the objects in my map?
adr
 
Posts: 532
Joined: Wed Oct 12, 2005 8:26 pm
Location: USA

Postby lethe » Sat Mar 14, 2009 9:57 am

I've done some fiddling with treeforms version of the octree script. I made a bunch of changes, the most important being the originals assumption that you would only want to use an octree for collision detection - it now allows you to generate an octree keeping its materials/textures and without sticking 'barrier' on everything. Basically run it without the -c option for a rendering version, with the -c version for a collision detection version. An extra parameter - maximum recursion depth, has also been added. Its also gained a couple of minor bug fixes.

Whilst I have tested it on a bunch of stuff I don't exactly have many meshes of the sort you would really throw at this kind of script lying around, so can't be sure I've selected good default parameters, or that all bugs are toast. In case anyone is wondering why I'm playing with this I'll let you guess what chickens next feature is going to be;-)

Get it here: http://thaines.net/content/panda3d/eggoctree.py
lethe
 
Posts: 524
Joined: Wed Jul 18, 2007 3:55 pm
Location: London, England

Postby ThomasEgi » Sat Mar 14, 2009 3:22 pm

are you trying to use octrees on visual geometry? or why did you make those changes?
User avatar
ThomasEgi
 
Posts: 2163
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby lethe » Sat Mar 14, 2009 3:36 pm

Not right now, but applying octrees is good for large levels, especially if you can set the far clipping plane close compared to the level size - i.e. in the tight and windy corridor type of fps. I'm trying to make Blender into a viable level editor - and it now has octree support for the output egg file, suitable for both visible geometry or collision detection geometry, at least in the current svn version. Might do a release tomorrow, but I want to clean up the manual first and not sure if I will get the time.
lethe
 
Posts: 524
Joined: Wed Jul 18, 2007 3:55 pm
Location: London, England

Postby Metal3d » Thu Apr 23, 2009 6:30 am

Im wondering if exporting my terrain.egg could benefit from octree. I have created a ~19000 poly terrain mesh, exported it out of blender chicken r60, and into ynjh_jo's roaming ralph variation with gravity (http://www.panda3d.org/phpbb2/viewtopic.php?t=2924). Everything seems great, except that sometimes, on inclines mostly but not always, poor little ralph can "push" through the terrain and back again.

Can Egg Octree possibly be a soloution to this? Would using a geomipterrain be possible/better?
User avatar
Metal3d
 
Posts: 32
Joined: Sat Mar 14, 2009 5:18 am

Postby treeform » Thu Apr 23, 2009 6:41 pm

Egg occtrees do little for for visible geometry. I would only use them for collision only. If you want terrain use GeoMipMap.
User avatar
treeform
 
Posts: 2106
Joined: Sat May 05, 2007 5:15 pm
Location: SF, CA

Postby lethe » Fri Apr 24, 2009 2:02 am

To actually answer metel3d's question, no - neither octrees nor the use of geomipterrain fix falling through the terrain - that is a failure of the collision system where you fall through a crack between the edges of polygons. The usual solution is to use multiple collision detection calls, so if one fails another will stop you falling through. (i.e. fire down multiple spaced out rays for instance.) I would use the geomipterrain to speed up basic rendering though.

Treeform: There are many situations where using octrees for your visible environment will result in a useful speed up - its dependent on the environment in question. This is especially the case for large dense environments where forward visibility is limited - there it could be the difference between playable and not.
lethe
 
Posts: 524
Joined: Wed Jul 18, 2007 3:55 pm
Location: London, England

PreviousNext

Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 0 guests