Add sample programs to the repository
|
|
@ -1,4 +1,3 @@
|
|||
/built_x64
|
||||
/built
|
||||
/samples
|
||||
/thirdparty
|
||||
|
|
|
|||
|
|
@ -0,0 +1,399 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Shao Zhang, Phil Saltzman, and Greg Lindley
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This tutorial demonstrates the use of tasks. A task is a function that
|
||||
# gets called once every frame. They are good for things that need to be
|
||||
# updated very often. In the case of asteroids, we use tasks to update
|
||||
# the positions of all the objects, and to check if the bullets or the
|
||||
# ship have hit the asteroids.
|
||||
#
|
||||
# Note: This definitely a complicated example. Tasks are the cores of
|
||||
# most games so it seemed appropriate to show what a full game in Panda
|
||||
# could look like.
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import TextNode, TransparencyAttrib
|
||||
from panda3d.core import LPoint3, LVector3
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.task.Task import Task
|
||||
from math import sin, cos, pi
|
||||
from random import randint, choice, random
|
||||
from direct.interval.MetaInterval import Sequence
|
||||
from direct.interval.FunctionInterval import Wait, Func
|
||||
import sys
|
||||
|
||||
# Constants that will control the behavior of the game. It is good to
|
||||
# group constants like this so that they can be changed once without
|
||||
# having to find everywhere they are used in code
|
||||
SPRITE_POS = 55 # At default field of view and a depth of 55, the screen
|
||||
# dimensions is 40x30 units
|
||||
SCREEN_X = 20 # Screen goes from -20 to 20 on X
|
||||
SCREEN_Y = 15 # Screen goes from -15 to 15 on Y
|
||||
TURN_RATE = 360 # Degrees ship can turn in 1 second
|
||||
ACCELERATION = 10 # Ship acceleration in units/sec/sec
|
||||
MAX_VEL = 6 # Maximum ship velocity in units/sec
|
||||
MAX_VEL_SQ = MAX_VEL ** 2 # Square of the ship velocity
|
||||
DEG_TO_RAD = pi / 180 # translates degrees to radians for sin and cos
|
||||
BULLET_LIFE = 2 # How long bullets stay on screen before removed
|
||||
BULLET_REPEAT = .2 # How often bullets can be fired
|
||||
BULLET_SPEED = 10 # Speed bullets move
|
||||
AST_INIT_VEL = 1 # Velocity of the largest asteroids
|
||||
AST_INIT_SCALE = 3 # Initial asteroid scale
|
||||
AST_VEL_SCALE = 2.2 # How much asteroid speed multiplies when broken up
|
||||
AST_SIZE_SCALE = .6 # How much asteroid scale changes when broken up
|
||||
AST_MIN_SCALE = 1.1 # If and asteroid is smaller than this and is hit,
|
||||
# it disapears instead of splitting up
|
||||
|
||||
|
||||
# This helps reduce the amount of code used by loading objects, since all of
|
||||
# the objects are pretty much the same.
|
||||
def loadObject(tex=None, pos=LPoint3(0, 0), depth=SPRITE_POS, scale=1,
|
||||
transparency=True):
|
||||
# Every object uses the plane model and is parented to the camera
|
||||
# so that it faces the screen.
|
||||
obj = loader.loadModel("models/plane")
|
||||
obj.reparentTo(camera)
|
||||
|
||||
# Set the initial position and scale.
|
||||
obj.setPos(pos.getX(), depth, pos.getY())
|
||||
obj.setScale(scale)
|
||||
|
||||
# This tells Panda not to worry about the order that things are drawn in
|
||||
# (ie. disable Z-testing). This prevents an effect known as Z-fighting.
|
||||
obj.setBin("unsorted", 0)
|
||||
obj.setDepthTest(False)
|
||||
|
||||
if transparency:
|
||||
# Enable transparency blending.
|
||||
obj.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
if tex:
|
||||
# Load and set the requested texture.
|
||||
tex = loader.loadTexture("textures/" + tex)
|
||||
obj.setTexture(tex, 1)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
# Macro-like function used to reduce the amount to code needed to create the
|
||||
# on screen instructions
|
||||
def genLabelText(text, i):
|
||||
return OnscreenText(text=text, parent=base.a2dTopLeft, pos=(0.07, -.06 * i - 0.1),
|
||||
fg=(1, 1, 1, 1), align=TextNode.ALeft, shadow=(0, 0, 0, 0.5), scale=.05)
|
||||
|
||||
|
||||
class AsteroidsDemo(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# This code puts the standard title and instruction text on screen
|
||||
self.title = OnscreenText(text="Panda3D: Tutorial - Tasks",
|
||||
parent=base.a2dBottomRight, scale=.07,
|
||||
align=TextNode.ARight, pos=(-0.1, 0.1),
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 0.5))
|
||||
self.escapeText = genLabelText("ESC: Quit", 0)
|
||||
self.leftkeyText = genLabelText("[Left Arrow]: Turn Left (CCW)", 1)
|
||||
self.rightkeyText = genLabelText("[Right Arrow]: Turn Right (CW)", 2)
|
||||
self.upkeyText = genLabelText("[Up Arrow]: Accelerate", 3)
|
||||
self.spacekeyText = genLabelText("[Space Bar]: Fire", 4)
|
||||
|
||||
# Disable default mouse-based camera control. This is a method on the
|
||||
# ShowBase class from which we inherit.
|
||||
self.disableMouse()
|
||||
|
||||
# Load the background starfield.
|
||||
self.setBackgroundColor((0, 0, 0, 1))
|
||||
self.bg = loadObject("stars.jpg", scale=146, depth=200,
|
||||
transparency=False)
|
||||
|
||||
# Load the ship and set its initial velocity.
|
||||
self.ship = loadObject("ship.png")
|
||||
self.setVelocity(self.ship, LVector3.zero())
|
||||
|
||||
# A dictionary of what keys are currently being pressed
|
||||
# The key events update this list, and our task will query it as input
|
||||
self.keys = {"turnLeft": 0, "turnRight": 0,
|
||||
"accel": 0, "fire": 0}
|
||||
|
||||
self.accept("escape", sys.exit) # Escape quits
|
||||
# Other keys events set the appropriate value in our key dictionary
|
||||
self.accept("arrow_left", self.setKey, ["turnLeft", 1])
|
||||
self.accept("arrow_left-up", self.setKey, ["turnLeft", 0])
|
||||
self.accept("arrow_right", self.setKey, ["turnRight", 1])
|
||||
self.accept("arrow_right-up", self.setKey, ["turnRight", 0])
|
||||
self.accept("arrow_up", self.setKey, ["accel", 1])
|
||||
self.accept("arrow_up-up", self.setKey, ["accel", 0])
|
||||
self.accept("space", self.setKey, ["fire", 1])
|
||||
|
||||
# Now we create the task. taskMgr is the task manager that actually
|
||||
# calls the function each frame. The add method creates a new task.
|
||||
# The first argument is the function to be called, and the second
|
||||
# argument is the name for the task. It returns a task object which
|
||||
# is passed to the function each frame.
|
||||
self.gameTask = taskMgr.add(self.gameLoop, "gameLoop")
|
||||
|
||||
# Stores the time at which the next bullet may be fired.
|
||||
self.nextBullet = 0.0
|
||||
|
||||
# This list will stored fired bullets.
|
||||
self.bullets = []
|
||||
|
||||
# Complete initialization by spawning the asteroids.
|
||||
self.spawnAsteroids()
|
||||
|
||||
# As described earlier, this simply sets a key in the self.keys dictionary
|
||||
# to the given value.
|
||||
def setKey(self, key, val):
|
||||
self.keys[key] = val
|
||||
|
||||
def setVelocity(self, obj, val):
|
||||
obj.setPythonTag("velocity", val)
|
||||
|
||||
def getVelocity(self, obj):
|
||||
return obj.getPythonTag("velocity")
|
||||
|
||||
def setExpires(self, obj, val):
|
||||
obj.setPythonTag("expires", val)
|
||||
|
||||
def getExpires(self, obj):
|
||||
return obj.getPythonTag("expires")
|
||||
|
||||
def spawnAsteroids(self):
|
||||
# Control variable for if the ship is alive
|
||||
self.alive = True
|
||||
self.asteroids = [] # List that will contain our asteroids
|
||||
|
||||
for i in range(10):
|
||||
# This loads an asteroid. The texture chosen is random
|
||||
# from "asteroid1.png" to "asteroid3.png".
|
||||
asteroid = loadObject("asteroid%d.png" % (randint(1, 3)),
|
||||
scale=AST_INIT_SCALE)
|
||||
self.asteroids.append(asteroid)
|
||||
|
||||
# This is kind of a hack, but it keeps the asteroids from spawning
|
||||
# near the player. It creates the list (-20, -19 ... -5, 5, 6, 7,
|
||||
# ... 20) and chooses a value from it. Since the player starts at 0
|
||||
# and this list doesn't contain anything from -4 to 4, it won't be
|
||||
# close to the player.
|
||||
asteroid.setX(choice(range(-SCREEN_X, -5) + range(5, SCREEN_X)))
|
||||
# Same thing for Y, but from -15 to 15
|
||||
asteroid.setZ(choice(range(-SCREEN_Y, -5) + range(5, SCREEN_Y)))
|
||||
|
||||
# Heading is a random angle in radians
|
||||
heading = random() * 2 * pi
|
||||
|
||||
# Converts the heading to a vector and multiplies it by speed to
|
||||
# get a velocity vector
|
||||
v = LVector3(sin(heading), 0, cos(heading)) * AST_INIT_VEL
|
||||
self.setVelocity(self.asteroids[i], v)
|
||||
|
||||
# This is our main task function, which does all of the per-frame
|
||||
# processing. It takes in self like all functions in a class, and task,
|
||||
# the task object returned by taskMgr.
|
||||
def gameLoop(self, task):
|
||||
# Get the time elapsed since the next frame. We need this for our
|
||||
# distance and velocity calculations.
|
||||
dt = globalClock.getDt()
|
||||
|
||||
# If the ship is not alive, do nothing. Tasks return Task.cont to
|
||||
# signify that the task should continue running. If Task.done were
|
||||
# returned instead, the task would be removed and would no longer be
|
||||
# called every frame.
|
||||
if not self.alive:
|
||||
return Task.cont
|
||||
|
||||
# update ship position
|
||||
self.updateShip(dt)
|
||||
|
||||
# check to see if the ship can fire
|
||||
if self.keys["fire"] and task.time > self.nextBullet:
|
||||
self.fire(task.time) # If so, call the fire function
|
||||
# And disable firing for a bit
|
||||
self.nextBullet = task.time + BULLET_REPEAT
|
||||
# Remove the fire flag until the next spacebar press
|
||||
self.keys["fire"] = 0
|
||||
|
||||
# update asteroids
|
||||
for obj in self.asteroids:
|
||||
self.updatePos(obj, dt)
|
||||
|
||||
# update bullets
|
||||
newBulletArray = []
|
||||
for obj in self.bullets:
|
||||
self.updatePos(obj, dt) # Update the bullet
|
||||
# Bullets have an experation time (see definition of fire)
|
||||
# If a bullet has not expired, add it to the new bullet list so
|
||||
# that it will continue to exist.
|
||||
if self.getExpires(obj) > task.time:
|
||||
newBulletArray.append(obj)
|
||||
else:
|
||||
obj.removeNode() # Otherwise, remove it from the scene.
|
||||
# Set the bullet array to be the newly updated array
|
||||
self.bullets = newBulletArray
|
||||
|
||||
# Check bullet collision with asteroids
|
||||
# In short, it checks every bullet against every asteroid. This is
|
||||
# quite slow. A big optimization would be to sort the objects left to
|
||||
# right and check only if they overlap. Framerate can go way down if
|
||||
# there are many bullets on screen, but for the most part it's okay.
|
||||
for bullet in self.bullets:
|
||||
# This range statement makes it step though the asteroid list
|
||||
# backwards. This is because if an asteroid is removed, the
|
||||
# elements after it will change position in the list. If you go
|
||||
# backwards, the length stays constant.
|
||||
for i in range(len(self.asteroids) - 1, -1, -1):
|
||||
asteroid = self.asteroids[i]
|
||||
# Panda's collision detection is more complicated than we need
|
||||
# here. This is the basic sphere collision check. If the
|
||||
# distance between the object centers is less than sum of the
|
||||
# radii of the two objects, then we have a collision. We use
|
||||
# lengthSquared() since it is faster than length().
|
||||
if ((bullet.getPos() - asteroid.getPos()).lengthSquared() <
|
||||
(((bullet.getScale().getX() + asteroid.getScale().getX())
|
||||
* .5) ** 2)):
|
||||
# Schedule the bullet for removal
|
||||
self.setExpires(bullet, 0)
|
||||
self.asteroidHit(i) # Handle the hit
|
||||
|
||||
# Now we do the same collision pass for the ship
|
||||
shipSize = self.ship.getScale().getX()
|
||||
for ast in self.asteroids:
|
||||
# Same sphere collision check for the ship vs. the asteroid
|
||||
if ((self.ship.getPos() - ast.getPos()).lengthSquared() <
|
||||
(((shipSize + ast.getScale().getX()) * .5) ** 2)):
|
||||
# If there is a hit, clear the screen and schedule a restart
|
||||
self.alive = False # Ship is no longer alive
|
||||
# Remove every object in asteroids and bullets from the scene
|
||||
for i in self.asteroids + self.bullets:
|
||||
i.removeNode()
|
||||
self.bullets = [] # Clear the bullet list
|
||||
self.ship.hide() # Hide the ship
|
||||
# Reset the velocity
|
||||
self.setVelocity(self.ship, LVector3(0, 0, 0))
|
||||
Sequence(Wait(2), # Wait 2 seconds
|
||||
Func(self.ship.setR, 0), # Reset heading
|
||||
Func(self.ship.setX, 0), # Reset position X
|
||||
# Reset position Y (Z for Panda)
|
||||
Func(self.ship.setZ, 0),
|
||||
Func(self.ship.show), # Show the ship
|
||||
Func(self.spawnAsteroids)).start() # Remake asteroids
|
||||
return Task.cont
|
||||
|
||||
# If the player has successfully destroyed all asteroids, respawn them
|
||||
if len(self.asteroids) == 0:
|
||||
self.spawnAsteroids()
|
||||
|
||||
return Task.cont # Since every return is Task.cont, the task will
|
||||
# continue indefinitely
|
||||
|
||||
# Updates the positions of objects
|
||||
def updatePos(self, obj, dt):
|
||||
vel = self.getVelocity(obj)
|
||||
newPos = obj.getPos() + (vel * dt)
|
||||
|
||||
# Check if the object is out of bounds. If so, wrap it
|
||||
radius = .5 * obj.getScale().getX()
|
||||
if newPos.getX() - radius > SCREEN_X:
|
||||
newPos.setX(-SCREEN_X)
|
||||
elif newPos.getX() + radius < -SCREEN_X:
|
||||
newPos.setX(SCREEN_X)
|
||||
if newPos.getZ() - radius > SCREEN_Y:
|
||||
newPos.setZ(-SCREEN_Y)
|
||||
elif newPos.getZ() + radius < -SCREEN_Y:
|
||||
newPos.setZ(SCREEN_Y)
|
||||
|
||||
obj.setPos(newPos)
|
||||
|
||||
# The handler when an asteroid is hit by a bullet
|
||||
def asteroidHit(self, index):
|
||||
# If the asteroid is small it is simply removed
|
||||
if self.asteroids[index].getScale().getX() <= AST_MIN_SCALE:
|
||||
self.asteroids[index].removeNode()
|
||||
# Remove the asteroid from the list of asteroids.
|
||||
del self.asteroids[index]
|
||||
else:
|
||||
# If it is big enough, divide it up into little asteroids.
|
||||
# First we update the current asteroid.
|
||||
asteroid = self.asteroids[index]
|
||||
newScale = asteroid.getScale().getX() * AST_SIZE_SCALE
|
||||
asteroid.setScale(newScale) # Rescale it
|
||||
|
||||
# The new direction is chosen as perpendicular to the old direction
|
||||
# This is determined using the cross product, which returns a
|
||||
# vector perpendicular to the two input vectors. By crossing
|
||||
# velocity with a vector that goes into the screen, we get a vector
|
||||
# that is orthagonal to the original velocity in the screen plane.
|
||||
vel = self.getVelocity(asteroid)
|
||||
speed = vel.length() * AST_VEL_SCALE
|
||||
vel.normalize()
|
||||
vel = LVector3(0, 1, 0).cross(vel)
|
||||
vel *= speed
|
||||
self.setVelocity(asteroid, vel)
|
||||
|
||||
# Now we create a new asteroid identical to the current one
|
||||
newAst = loadObject(scale=newScale)
|
||||
self.setVelocity(newAst, vel * -1)
|
||||
newAst.setPos(asteroid.getPos())
|
||||
newAst.setTexture(asteroid.getTexture(), 1)
|
||||
self.asteroids.append(newAst)
|
||||
|
||||
# This updates the ship's position. This is similar to the general update
|
||||
# but takes into account turn and thrust
|
||||
def updateShip(self, dt):
|
||||
heading = self.ship.getR() # Heading is the roll value for this model
|
||||
# Change heading if left or right is being pressed
|
||||
if self.keys["turnRight"]:
|
||||
heading += dt * TURN_RATE
|
||||
self.ship.setR(heading % 360)
|
||||
elif self.keys["turnLeft"]:
|
||||
heading -= dt * TURN_RATE
|
||||
self.ship.setR(heading % 360)
|
||||
|
||||
# Thrust causes acceleration in the direction the ship is currently
|
||||
# facing
|
||||
if self.keys["accel"]:
|
||||
heading_rad = DEG_TO_RAD * heading
|
||||
# This builds a new velocity vector and adds it to the current one
|
||||
# relative to the camera, the screen in Panda is the XZ plane.
|
||||
# Therefore all of our Y values in our velocities are 0 to signify
|
||||
# no change in that direction.
|
||||
newVel = \
|
||||
LVector3(sin(heading_rad), 0, cos(heading_rad)) * ACCELERATION * dt
|
||||
newVel += self.getVelocity(self.ship)
|
||||
# Clamps the new velocity to the maximum speed. lengthSquared() is
|
||||
# used again since it is faster than length()
|
||||
if newVel.lengthSquared() > MAX_VEL_SQ:
|
||||
newVel.normalize()
|
||||
newVel *= MAX_VEL
|
||||
self.setVelocity(self.ship, newVel)
|
||||
|
||||
# Finally, update the position as with any other object
|
||||
self.updatePos(self.ship, dt)
|
||||
|
||||
# Creates a bullet and adds it to the bullet list
|
||||
def fire(self, time):
|
||||
direction = DEG_TO_RAD * self.ship.getR()
|
||||
pos = self.ship.getPos()
|
||||
bullet = loadObject("bullet.png", scale=.2) # Create the object
|
||||
bullet.setPos(pos)
|
||||
# Velocity is in relation to the ship
|
||||
vel = (self.getVelocity(self.ship) +
|
||||
(LVector3(sin(direction), 0, cos(direction)) *
|
||||
BULLET_SPEED))
|
||||
self.setVelocity(bullet, vel)
|
||||
# Set the bullet expiration time to be a certain amount past the
|
||||
# current time
|
||||
self.setExpires(bullet, time + BULLET_LIFE)
|
||||
|
||||
# Finally, add the new bullet to the list
|
||||
self.bullets.append(bullet)
|
||||
|
||||
# We now have everything we need. Make an instance of the class and start
|
||||
# 3D rendering
|
||||
demo = AsteroidsDemo()
|
||||
demo.run()
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<CoordinateSystem> { Y-Up }
|
||||
|
||||
<Comment> {
|
||||
"maya2egg plane.mb plane.egg"
|
||||
}
|
||||
<Group> groundPlane_transform {
|
||||
}
|
||||
<Group> pPlane1 {
|
||||
<VertexPool> pPlaneShape1.verts {
|
||||
<Vertex> 1 {
|
||||
-0.5 -0.5 0
|
||||
<Normal> { 0 0 -1 }
|
||||
<UV> { 0 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.5 0.5 0
|
||||
<Normal> { 0 0 -1 }
|
||||
<UV> { 0 1 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.5 -0.5 0
|
||||
<Normal> { 0 0 -1 }
|
||||
<UV> { 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
0.5 0.5 0
|
||||
<Normal> { 0 0 -1 }
|
||||
<UV> { 1 1 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0 0 -1 }
|
||||
<VertexRef>{ 3 4 2 1 <Ref> { pPlaneShape1.verts } }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
|
@ -0,0 +1,338 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Shao Zhang, Phil Saltzman
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This tutorial shows how to detect and respond to collisions. It uses solids
|
||||
# create in code and the egg files, how to set up collision masks, a traverser,
|
||||
# and a handler, how to detect collisions, and how to dispatch function based
|
||||
# on the collisions. All of this is put together to simulate a labyrinth-style
|
||||
# game
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import CollisionTraverser, CollisionNode
|
||||
from panda3d.core import CollisionHandlerQueue, CollisionRay
|
||||
from panda3d.core import Material, LRotationf, NodePath
|
||||
from panda3d.core import AmbientLight, DirectionalLight
|
||||
from panda3d.core import TextNode
|
||||
from panda3d.core import LVector3, BitMask32
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.interval.MetaInterval import Sequence, Parallel
|
||||
from direct.interval.LerpInterval import LerpFunc
|
||||
from direct.interval.FunctionInterval import Func, Wait
|
||||
from direct.task.Task import Task
|
||||
import sys
|
||||
|
||||
# Some constants for the program
|
||||
ACCEL = 70 # Acceleration in ft/sec/sec
|
||||
MAX_SPEED = 5 # Max speed in ft/sec
|
||||
MAX_SPEED_SQ = MAX_SPEED ** 2 # Squared to make it easier to use lengthSquared
|
||||
# Instead of length
|
||||
|
||||
|
||||
class BallInMazeDemo(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# This code puts the standard title and instruction text on screen
|
||||
self.title = \
|
||||
OnscreenText(text="Panda3D: Tutorial - Collision Detection",
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08,
|
||||
shadow=(0, 0, 0, 0.5))
|
||||
self.instructions = \
|
||||
OnscreenText(text="Mouse pointer tilts the board",
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.05, -0.08), fg=(1, 1, 1, 1), scale=.06,
|
||||
shadow=(0, 0, 0, 0.5))
|
||||
|
||||
self.accept("escape", sys.exit) # Escape quits
|
||||
|
||||
# Disable default mouse-based camera control. This is a method on the
|
||||
# ShowBase class from which we inherit.
|
||||
self.disableMouse()
|
||||
camera.setPosHpr(0, 0, 25, 0, -90, 0) # Place the camera
|
||||
|
||||
# Load the maze and place it in the scene
|
||||
self.maze = loader.loadModel("models/maze")
|
||||
self.maze.reparentTo(render)
|
||||
|
||||
# Most times, you want collisions to be tested against invisible geometry
|
||||
# rather than every polygon. This is because testing against every polygon
|
||||
# in the scene is usually too slow. You can have simplified or approximate
|
||||
# geometry for the solids and still get good results.
|
||||
#
|
||||
# Sometimes you'll want to create and position your own collision solids in
|
||||
# code, but it's often easier to have them built automatically. This can be
|
||||
# done by adding special tags into an egg file. Check maze.egg and ball.egg
|
||||
# and look for lines starting with <Collide>. The part is brackets tells
|
||||
# Panda exactly what to do. Polyset means to use the polygons in that group
|
||||
# as solids, while Sphere tells panda to make a collision sphere around them
|
||||
# Keep means to keep the polygons in the group as visable geometry (good
|
||||
# for the ball, not for the triggers), and descend means to make sure that
|
||||
# the settings are applied to any subgroups.
|
||||
#
|
||||
# Once we have the collision tags in the models, we can get to them using
|
||||
# NodePath's find command
|
||||
|
||||
# Find the collision node named wall_collide
|
||||
self.walls = self.maze.find("**/wall_collide")
|
||||
|
||||
# Collision objects are sorted using BitMasks. BitMasks are ordinary numbers
|
||||
# with extra methods for working with them as binary bits. Every collision
|
||||
# solid has both a from mask and an into mask. Before Panda tests two
|
||||
# objects, it checks to make sure that the from and into collision masks
|
||||
# have at least one bit in common. That way things that shouldn't interact
|
||||
# won't. Normal model nodes have collision masks as well. By default they
|
||||
# are set to bit 20. If you want to collide against actual visable polygons,
|
||||
# set a from collide mask to include bit 20
|
||||
#
|
||||
# For this example, we will make everything we want the ball to collide with
|
||||
# include bit 0
|
||||
self.walls.node().setIntoCollideMask(BitMask32.bit(0))
|
||||
# CollisionNodes are usually invisible but can be shown. Uncomment the next
|
||||
# line to see the collision walls
|
||||
#self.walls.show()
|
||||
|
||||
# We will now find the triggers for the holes and set their masks to 0 as
|
||||
# well. We also set their names to make them easier to identify during
|
||||
# collisions
|
||||
self.loseTriggers = []
|
||||
for i in range(6):
|
||||
trigger = self.maze.find("**/hole_collide" + str(i))
|
||||
trigger.node().setIntoCollideMask(BitMask32.bit(0))
|
||||
trigger.node().setName("loseTrigger")
|
||||
self.loseTriggers.append(trigger)
|
||||
# Uncomment this line to see the triggers
|
||||
# trigger.show()
|
||||
|
||||
# Ground_collide is a single polygon on the same plane as the ground in the
|
||||
# maze. We will use a ray to collide with it so that we will know exactly
|
||||
# what height to put the ball at every frame. Since this is not something
|
||||
# that we want the ball itself to collide with, it has a different
|
||||
# bitmask.
|
||||
self.mazeGround = self.maze.find("**/ground_collide")
|
||||
self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1))
|
||||
|
||||
# Load the ball and attach it to the scene
|
||||
# It is on a root dummy node so that we can rotate the ball itself without
|
||||
# rotating the ray that will be attached to it
|
||||
self.ballRoot = render.attachNewNode("ballRoot")
|
||||
self.ball = loader.loadModel("models/ball")
|
||||
self.ball.reparentTo(self.ballRoot)
|
||||
|
||||
# Find the collison sphere for the ball which was created in the egg file
|
||||
# Notice that it has a from collision mask of bit 0, and an into collison
|
||||
# mask of no bits. This means that the ball can only cause collisions, not
|
||||
# be collided into
|
||||
self.ballSphere = self.ball.find("**/ball")
|
||||
self.ballSphere.node().setFromCollideMask(BitMask32.bit(0))
|
||||
self.ballSphere.node().setIntoCollideMask(BitMask32.allOff())
|
||||
|
||||
# No we create a ray to start above the ball and cast down. This is to
|
||||
# Determine the height the ball should be at and the angle the floor is
|
||||
# tilting. We could have used the sphere around the ball itself, but it
|
||||
# would not be as reliable
|
||||
self.ballGroundRay = CollisionRay() # Create the ray
|
||||
self.ballGroundRay.setOrigin(0, 0, 10) # Set its origin
|
||||
self.ballGroundRay.setDirection(0, 0, -1) # And its direction
|
||||
# Collision solids go in CollisionNode
|
||||
# Create and name the node
|
||||
self.ballGroundCol = CollisionNode('groundRay')
|
||||
self.ballGroundCol.addSolid(self.ballGroundRay) # Add the ray
|
||||
self.ballGroundCol.setFromCollideMask(
|
||||
BitMask32.bit(1)) # Set its bitmasks
|
||||
self.ballGroundCol.setIntoCollideMask(BitMask32.allOff())
|
||||
# Attach the node to the ballRoot so that the ray is relative to the ball
|
||||
# (it will always be 10 feet over the ball and point down)
|
||||
self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol)
|
||||
# Uncomment this line to see the ray
|
||||
#self.ballGroundColNp.show()
|
||||
|
||||
# Finally, we create a CollisionTraverser. CollisionTraversers are what
|
||||
# do the job of walking the scene graph and calculating collisions.
|
||||
# For a traverser to actually do collisions, you need to call
|
||||
# traverser.traverse() on a part of the scene. Fortunately, ShowBase
|
||||
# has a task that does this for the entire scene once a frame. By
|
||||
# assigning it to self.cTrav, we designate that this is the one that
|
||||
# it should call traverse() on each frame.
|
||||
self.cTrav = CollisionTraverser()
|
||||
|
||||
# Collision traversers tell collision handlers about collisions, and then
|
||||
# the handler decides what to do with the information. We are using a
|
||||
# CollisionHandlerQueue, which simply creates a list of all of the
|
||||
# collisions in a given pass. There are more sophisticated handlers like
|
||||
# one that sends events and another that tries to keep collided objects
|
||||
# apart, but the results are often better with a simple queue
|
||||
self.cHandler = CollisionHandlerQueue()
|
||||
# Now we add the collision nodes that can create a collision to the
|
||||
# traverser. The traverser will compare these to all others nodes in the
|
||||
# scene. There is a limit of 32 CollisionNodes per traverser
|
||||
# We add the collider, and the handler to use as a pair
|
||||
self.cTrav.addCollider(self.ballSphere, self.cHandler)
|
||||
self.cTrav.addCollider(self.ballGroundColNp, self.cHandler)
|
||||
|
||||
# Collision traversers have a built in tool to help visualize collisions.
|
||||
# Uncomment the next line to see it.
|
||||
#self.cTrav.showCollisions(render)
|
||||
|
||||
# This section deals with lighting for the ball. Only the ball was lit
|
||||
# because the maze has static lighting pregenerated by the modeler
|
||||
ambientLight = AmbientLight("ambientLight")
|
||||
ambientLight.setColor((.55, .55, .55, 1))
|
||||
directionalLight = DirectionalLight("directionalLight")
|
||||
directionalLight.setDirection(LVector3(0, 0, -1))
|
||||
directionalLight.setColor((0.375, 0.375, 0.375, 1))
|
||||
directionalLight.setSpecularColor((1, 1, 1, 1))
|
||||
self.ballRoot.setLight(render.attachNewNode(ambientLight))
|
||||
self.ballRoot.setLight(render.attachNewNode(directionalLight))
|
||||
|
||||
# This section deals with adding a specular highlight to the ball to make
|
||||
# it look shiny. Normally, this is specified in the .egg file.
|
||||
m = Material()
|
||||
m.setSpecular((1, 1, 1, 1))
|
||||
m.setShininess(96)
|
||||
self.ball.setMaterial(m, 1)
|
||||
|
||||
# Finally, we call start for more initialization
|
||||
self.start()
|
||||
|
||||
def start(self):
|
||||
# The maze model also has a locator in it for where to start the ball
|
||||
# To access it we use the find command
|
||||
startPos = self.maze.find("**/start").getPos()
|
||||
# Set the ball in the starting position
|
||||
self.ballRoot.setPos(startPos)
|
||||
self.ballV = LVector3(0, 0, 0) # Initial velocity is 0
|
||||
self.accelV = LVector3(0, 0, 0) # Initial acceleration is 0
|
||||
|
||||
# Create the movement task, but first make sure it is not already
|
||||
# running
|
||||
taskMgr.remove("rollTask")
|
||||
self.mainLoop = taskMgr.add(self.rollTask, "rollTask")
|
||||
|
||||
# This function handles the collision between the ray and the ground
|
||||
# Information about the interaction is passed in colEntry
|
||||
def groundCollideHandler(self, colEntry):
|
||||
# Set the ball to the appropriate Z value for it to be exactly on the
|
||||
# ground
|
||||
newZ = colEntry.getSurfacePoint(render).getZ()
|
||||
self.ballRoot.setZ(newZ + .4)
|
||||
|
||||
# Find the acceleration direction. First the surface normal is crossed with
|
||||
# the up vector to get a vector perpendicular to the slope
|
||||
norm = colEntry.getSurfaceNormal(render)
|
||||
accelSide = norm.cross(LVector3.up())
|
||||
# Then that vector is crossed with the surface normal to get a vector that
|
||||
# points down the slope. By getting the acceleration in 3D like this rather
|
||||
# than in 2D, we reduce the amount of error per-frame, reducing jitter
|
||||
self.accelV = norm.cross(accelSide)
|
||||
|
||||
# This function handles the collision between the ball and a wall
|
||||
def wallCollideHandler(self, colEntry):
|
||||
# First we calculate some numbers we need to do a reflection
|
||||
norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall
|
||||
curSpeed = self.ballV.length() # The current speed
|
||||
inVec = self.ballV / curSpeed # The direction of travel
|
||||
velAngle = norm.dot(inVec) # Angle of incidance
|
||||
hitDir = colEntry.getSurfacePoint(render) - self.ballRoot.getPos()
|
||||
hitDir.normalize()
|
||||
# The angle between the ball and the normal
|
||||
hitAngle = norm.dot(hitDir)
|
||||
|
||||
# Ignore the collision if the ball is either moving away from the wall
|
||||
# already (so that we don't accidentally send it back into the wall)
|
||||
# and ignore it if the collision isn't dead-on (to avoid getting caught on
|
||||
# corners)
|
||||
if velAngle > 0 and hitAngle > .995:
|
||||
# Standard reflection equation
|
||||
reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec
|
||||
|
||||
# This makes the velocity half of what it was if the hit was dead-on
|
||||
# and nearly exactly what it was if this is a glancing blow
|
||||
self.ballV = reflectVec * (curSpeed * (((1 - velAngle) * .5) + .5))
|
||||
# Since we have a collision, the ball is already a little bit buried in
|
||||
# the wall. This calculates a vector needed to move it so that it is
|
||||
# exactly touching the wall
|
||||
disp = (colEntry.getSurfacePoint(render) -
|
||||
colEntry.getInteriorPoint(render))
|
||||
newPos = self.ballRoot.getPos() + disp
|
||||
self.ballRoot.setPos(newPos)
|
||||
|
||||
# This is the task that deals with making everything interactive
|
||||
def rollTask(self, task):
|
||||
# Standard technique for finding the amount of time since the last
|
||||
# frame
|
||||
dt = globalClock.getDt()
|
||||
|
||||
# If dt is large, then there has been a # hiccup that could cause the ball
|
||||
# to leave the field if this functions runs, so ignore the frame
|
||||
if dt > .2:
|
||||
return Task.cont
|
||||
|
||||
# The collision handler collects the collisions. We dispatch which function
|
||||
# to handle the collision based on the name of what was collided into
|
||||
for i in range(self.cHandler.getNumEntries()):
|
||||
entry = self.cHandler.getEntry(i)
|
||||
name = entry.getIntoNode().getName()
|
||||
if name == "wall_collide":
|
||||
self.wallCollideHandler(entry)
|
||||
elif name == "ground_collide":
|
||||
self.groundCollideHandler(entry)
|
||||
elif name == "loseTrigger":
|
||||
self.loseGame(entry)
|
||||
|
||||
# Read the mouse position and tilt the maze accordingly
|
||||
if base.mouseWatcherNode.hasMouse():
|
||||
mpos = base.mouseWatcherNode.getMouse() # get the mouse position
|
||||
self.maze.setP(mpos.getY() * -10)
|
||||
self.maze.setR(mpos.getX() * 10)
|
||||
|
||||
# Finally, we move the ball
|
||||
# Update the velocity based on acceleration
|
||||
self.ballV += self.accelV * dt * ACCEL
|
||||
# Clamp the velocity to the maximum speed
|
||||
if self.ballV.lengthSquared() > MAX_SPEED_SQ:
|
||||
self.ballV.normalize()
|
||||
self.ballV *= MAX_SPEED
|
||||
# Update the position based on the velocity
|
||||
self.ballRoot.setPos(self.ballRoot.getPos() + (self.ballV * dt))
|
||||
|
||||
# This block of code rotates the ball. It uses something called a quaternion
|
||||
# to rotate the ball around an arbitrary axis. That axis perpendicular to
|
||||
# the balls rotation, and the amount has to do with the size of the ball
|
||||
# This is multiplied on the previous rotation to incrimentally turn it.
|
||||
prevRot = LRotationf(self.ball.getQuat())
|
||||
axis = LVector3.up().cross(self.ballV)
|
||||
newRot = LRotationf(axis, 45.5 * dt * self.ballV.length())
|
||||
self.ball.setQuat(prevRot * newRot)
|
||||
|
||||
return Task.cont # Continue the task indefinitely
|
||||
|
||||
# If the ball hits a hole trigger, then it should fall in the hole.
|
||||
# This is faked rather than dealing with the actual physics of it.
|
||||
def loseGame(self, entry):
|
||||
# The triggers are set up so that the center of the ball should move to the
|
||||
# collision point to be in the hole
|
||||
toPos = entry.getInteriorPoint(render)
|
||||
taskMgr.remove('rollTask') # Stop the maze task
|
||||
|
||||
# Move the ball into the hole over a short sequence of time. Then wait a
|
||||
# second and call start to reset the game
|
||||
Sequence(
|
||||
Parallel(
|
||||
LerpFunc(self.ballRoot.setX, fromData=self.ballRoot.getX(),
|
||||
toData=toPos.getX(), duration=.1),
|
||||
LerpFunc(self.ballRoot.setY, fromData=self.ballRoot.getY(),
|
||||
toData=toPos.getY(), duration=.1),
|
||||
LerpFunc(self.ballRoot.setZ, fromData=self.ballRoot.getZ(),
|
||||
toData=self.ballRoot.getZ() - .9, duration=.2)),
|
||||
Wait(1),
|
||||
Func(self.start)).start()
|
||||
|
||||
# Finally, create an instance of our class and start 3d rendering
|
||||
demo = BallInMazeDemo()
|
||||
demo.run()
|
||||
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
|
@ -0,0 +1,199 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Shao Zhang, Phil Saltzman, and Eddie Caanan
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This tutorial shows how to play animations on models aka "actors".
|
||||
# It is based on the popular game of "Rock 'em Sock 'em Robots".
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import AmbientLight, DirectionalLight
|
||||
from panda3d.core import TextNode
|
||||
from panda3d.core import LVector3
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.interval.MetaInterval import Sequence
|
||||
from direct.interval.FunctionInterval import Func, Wait
|
||||
from direct.actor import Actor
|
||||
from random import random
|
||||
import sys
|
||||
|
||||
|
||||
class BoxingRobotDemo(ShowBase):
|
||||
# Macro-like function used to reduce the amount to code needed to create the
|
||||
# on screen instructions
|
||||
|
||||
def genLabelText(self, text, i):
|
||||
return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.05,
|
||||
pos=(0.1, - 0.1 -.07 * i), fg=(1, 1, 1, 1),
|
||||
align=TextNode.ALeft)
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# This code puts the standard title and instruction text on screen
|
||||
self.title = OnscreenText(text="Panda3D: Tutorial - Actors",
|
||||
parent=base.a2dBottomRight, style=1,
|
||||
fg=(0, 0, 0, 1), pos=(-0.2, 0.1),
|
||||
align=TextNode.ARight, scale=.09)
|
||||
|
||||
self.escapeEventText = self.genLabelText("ESC: Quit", 0)
|
||||
self.akeyEventText = self.genLabelText("[A]: Robot 1 Left Punch", 1)
|
||||
self.skeyEventText = self.genLabelText("[S]: Robot 1 Right Punch", 2)
|
||||
self.kkeyEventText = self.genLabelText("[K]: Robot 2 Left Punch", 3)
|
||||
self.lkeyEventText = self.genLabelText("[L]: Robot 2 Right Punch", 4)
|
||||
|
||||
# Set the camera in a fixed position
|
||||
self.disableMouse()
|
||||
camera.setPosHpr(14.5, -15.4, 14, 45, -14, 0)
|
||||
self.setBackgroundColor(0, 0, 0)
|
||||
|
||||
# Add lighting so that the objects are not drawn flat
|
||||
self.setupLights()
|
||||
|
||||
# Load the ring
|
||||
self.ring = loader.loadModel('models/ring')
|
||||
self.ring.reparentTo(render)
|
||||
|
||||
# Models that use skeletal animation are known as Actors instead of models
|
||||
# Instead of just one file, the have one file for the main model, and an
|
||||
# additional file for each playable animation.
|
||||
# They are loaded using Actor.Actor instead of loader.LoadModel.
|
||||
# The constructor takes the location of the main object as with a normal model
|
||||
# and a dictionary (A fancy python structure that is like a lookup table)
|
||||
# that contains names for animations, and paths to the appropriate
|
||||
# files
|
||||
self.robot1 = Actor.Actor('models/robot',
|
||||
{'leftPunch': 'models/robot_left_punch',
|
||||
'rightPunch': 'models/robot_right_punch',
|
||||
'headUp': 'models/robot_head_up',
|
||||
'headDown': 'models/robot_head_down'})
|
||||
|
||||
# Actors need to be positioned and parented like normal objects
|
||||
self.robot1.setPosHprScale(-1, -2.5, 4, 45, 0, 0, 1.25, 1.25, 1.25)
|
||||
self.robot1.reparentTo(render)
|
||||
|
||||
# We'll repeat the process for the second robot. The only thing that changes
|
||||
# here is the robot's color and position
|
||||
self.robot2 = Actor.Actor('models/robot',
|
||||
{'leftPunch': 'models/robot_left_punch',
|
||||
'rightPunch': 'models/robot_right_punch',
|
||||
'headUp': 'models/robot_head_up',
|
||||
'headDown': 'models/robot_head_down'})
|
||||
|
||||
# Set the properties of this robot
|
||||
self.robot2.setPosHprScale(1, 1.5, 4, 225, 0, 0, 1.25, 1.25, 1.25)
|
||||
self.robot2.setColor((.7, 0, 0, 1))
|
||||
self.robot2.reparentTo(render)
|
||||
|
||||
# Now we define how the animated models will move. Animations are played
|
||||
# through special intervals. In this case we use actor intervals in a
|
||||
# sequence to play the part of the punch animation where the arm extends,
|
||||
# call a function to check if the punch landed, and then play the part of the
|
||||
# animation where the arm retracts
|
||||
|
||||
# Punch sequence for robot 1's left arm
|
||||
self.robot1.punchLeft = Sequence(
|
||||
# Interval for the outstreched animation
|
||||
self.robot1.actorInterval('leftPunch', startFrame=1, endFrame=10),
|
||||
# Function to check if the punch was successful
|
||||
Func(self.checkPunch, 2),
|
||||
# Interval for the retract animation
|
||||
self.robot1.actorInterval('leftPunch', startFrame=11, endFrame=32))
|
||||
|
||||
# Punch sequence for robot 1's right arm
|
||||
self.robot1.punchRight = Sequence(
|
||||
self.robot1.actorInterval('rightPunch', startFrame=1, endFrame=10),
|
||||
Func(self.checkPunch, 2),
|
||||
self.robot1.actorInterval('rightPunch', startFrame=11, endFrame=32))
|
||||
|
||||
# Punch sequence for robot 2's left arm
|
||||
self.robot2.punchLeft = Sequence(
|
||||
self.robot2.actorInterval('leftPunch', startFrame=1, endFrame=10),
|
||||
Func(self.checkPunch, 1),
|
||||
self.robot2.actorInterval('leftPunch', startFrame=11, endFrame=32))
|
||||
|
||||
# Punch sequence for robot 2's right arm
|
||||
self.robot2.punchRight = Sequence(
|
||||
self.robot2.actorInterval('rightPunch', startFrame=1, endFrame=10),
|
||||
Func(self.checkPunch, 1),
|
||||
self.robot2.actorInterval('rightPunch', startFrame=11, endFrame=32))
|
||||
|
||||
# We use the same techinique to create a sequence for when a robot is knocked
|
||||
# out where the head pops up, waits a while, and then resets
|
||||
|
||||
# Head animation for robot 1
|
||||
self.robot1.resetHead = Sequence(
|
||||
# Interval for the head going up. Since no start or end frames were given,
|
||||
# the entire animation is played.
|
||||
self.robot1.actorInterval('headUp'),
|
||||
Wait(1.5),
|
||||
# The head down animation was animated a little too quickly, so this will
|
||||
# play it at 75% of it's normal speed
|
||||
self.robot1.actorInterval('headDown', playRate=.75))
|
||||
|
||||
# Head animation for robot 2
|
||||
self.robot2.resetHead = Sequence(
|
||||
self.robot2.actorInterval('headUp'),
|
||||
Wait(1.5),
|
||||
self.robot2.actorInterval('headDown', playRate=.75))
|
||||
|
||||
# Now that we have defined the motion, we can define our key input.
|
||||
# Each fist is bound to a key. When a key is pressed, self.tryPunch checks to
|
||||
# make sure that the both robots have their heads down, and if they do it
|
||||
# plays the given interval
|
||||
self.accept('escape', sys.exit)
|
||||
self.accept('a', self.tryPunch, [self.robot1.punchLeft])
|
||||
self.accept('s', self.tryPunch, [self.robot1.punchRight])
|
||||
self.accept('k', self.tryPunch, [self.robot2.punchLeft])
|
||||
self.accept('l', self.tryPunch, [self.robot2.punchRight])
|
||||
|
||||
# tryPunch will play the interval passed to it only if
|
||||
# neither robot has 'resetHead' playing (a head is up) AND
|
||||
# the punch interval passed to it is not already playing
|
||||
def tryPunch(self, interval):
|
||||
if (not self.robot1.resetHead.isPlaying() and
|
||||
not self.robot2.resetHead.isPlaying() and
|
||||
not interval.isPlaying()):
|
||||
interval.start()
|
||||
|
||||
# checkPunch will determine if a successful punch has been thrown
|
||||
def checkPunch(self, robot):
|
||||
if robot == 1:
|
||||
# punch is directed to robot 1
|
||||
# if robot 1 is playing'resetHead', do nothing
|
||||
if self.robot1.resetHead.isPlaying():
|
||||
return
|
||||
# if robot 1 is not punching...
|
||||
if (not self.robot1.punchLeft.isPlaying() and
|
||||
not self.robot1.punchRight.isPlaying()):
|
||||
# ...15% chance of successful hit
|
||||
if random() > .85:
|
||||
self.robot1.resetHead.start()
|
||||
# Otherwise, only 5% chance of sucessful hit
|
||||
elif random() > .95:
|
||||
self.robot1.resetHead.start()
|
||||
else:
|
||||
# punch is directed to robot 2, same as above
|
||||
if self.robot2.resetHead.isPlaying():
|
||||
return
|
||||
if (not self.robot2.punchLeft.isPlaying() and
|
||||
not self.robot2.punchRight.isPlaying()):
|
||||
if random() > .85:
|
||||
self.robot2.resetHead.start()
|
||||
elif random() > .95:
|
||||
self.robot2.resetHead.start()
|
||||
|
||||
# This function sets up the lighting
|
||||
def setupLights(self):
|
||||
ambientLight = AmbientLight("ambientLight")
|
||||
ambientLight.setColor((.8, .8, .75, 1))
|
||||
directionalLight = DirectionalLight("directionalLight")
|
||||
directionalLight.setDirection(LVector3(0, 0, -2.5))
|
||||
directionalLight.setColor((0.9, 0.8, 0.9, 1))
|
||||
render.setLight(render.attachNewNode(ambientLight))
|
||||
render.setLight(render.attachNewNode(directionalLight))
|
||||
|
||||
demo = BoxingRobotDemo()
|
||||
demo.run()
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Bump mapping is a way of making polygonal surfaces look
|
||||
# less flat. This sample uses normal mapping for all
|
||||
# surfaces, and also parallax mapping for the column.
|
||||
#
|
||||
# This is a tutorial to show how to do normal mapping
|
||||
# in panda3d using the Shader Generator.
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import loadPrcFileData
|
||||
from panda3d.core import WindowProperties
|
||||
from panda3d.core import Filename, Shader
|
||||
from panda3d.core import AmbientLight, PointLight
|
||||
from panda3d.core import TextNode
|
||||
from panda3d.core import LPoint3, LVector3
|
||||
from direct.task.Task import Task
|
||||
from direct.actor.Actor import Actor
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.filter.CommonFilters import *
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
# Function to put instructions on the screen.
|
||||
def addInstructions(pos, msg):
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05,
|
||||
shadow=(0, 0, 0, 1), parent=base.a2dTopLeft,
|
||||
pos=(0.08, -pos - 0.04), align=TextNode.ALeft)
|
||||
|
||||
# Function to put title on the screen.
|
||||
def addTitle(text):
|
||||
return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
pos=(-0.1, 0.09), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class BumpMapDemo(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Configure the parallax mapping settings (these are just the defaults)
|
||||
loadPrcFileData("", "parallax-mapping-samples 3\n"
|
||||
"parallax-mapping-scale 0.1")
|
||||
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# Check video card capabilities.
|
||||
if not self.win.getGsg().getSupportsBasicShaders():
|
||||
addTitle("Bump Mapping: "
|
||||
"Video driver reports that Cg shaders are not supported.")
|
||||
return
|
||||
|
||||
# Post the instructions
|
||||
self.title = addTitle("Panda3D: Tutorial - Bump Mapping")
|
||||
self.inst1 = addInstructions(0.06, "Press ESC to exit")
|
||||
self.inst2 = addInstructions(0.12, "Move mouse to rotate camera")
|
||||
self.inst3 = addInstructions(0.18, "Left mouse button: Move forwards")
|
||||
self.inst4 = addInstructions(0.24, "Right mouse button: Move backwards")
|
||||
self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off")
|
||||
|
||||
# Load the 'abstract room' model. This is a model of an
|
||||
# empty room containing a pillar, a pyramid, and a bunch
|
||||
# of exaggeratedly bumpy textures.
|
||||
|
||||
self.room = loader.loadModel("models/abstractroom")
|
||||
self.room.reparentTo(render)
|
||||
|
||||
# Make the mouse invisible, turn off normal mouse controls
|
||||
self.disableMouse()
|
||||
props = WindowProperties()
|
||||
props.setCursorHidden(True)
|
||||
self.win.requestProperties(props)
|
||||
self.camLens.setFov(60)
|
||||
|
||||
# Set the current viewing target
|
||||
self.focus = LVector3(55, -55, 20)
|
||||
self.heading = 180
|
||||
self.pitch = 0
|
||||
self.mousex = 0
|
||||
self.mousey = 0
|
||||
self.last = 0
|
||||
self.mousebtn = [0, 0, 0]
|
||||
|
||||
# Start the camera control task:
|
||||
taskMgr.add(self.controlCamera, "camera-task")
|
||||
self.accept("escape", sys.exit, [0])
|
||||
self.accept("mouse1", self.setMouseBtn, [0, 1])
|
||||
self.accept("mouse1-up", self.setMouseBtn, [0, 0])
|
||||
self.accept("mouse2", self.setMouseBtn, [1, 1])
|
||||
self.accept("mouse2-up", self.setMouseBtn, [1, 0])
|
||||
self.accept("mouse3", self.setMouseBtn, [2, 1])
|
||||
self.accept("mouse3-up", self.setMouseBtn, [2, 0])
|
||||
self.accept("enter", self.toggleShader)
|
||||
self.accept("j", self.rotateLight, [-1])
|
||||
self.accept("k", self.rotateLight, [1])
|
||||
self.accept("arrow_left", self.rotateCam, [-1])
|
||||
self.accept("arrow_right", self.rotateCam, [1])
|
||||
|
||||
# Add a light to the scene.
|
||||
self.lightpivot = render.attachNewNode("lightpivot")
|
||||
self.lightpivot.setPos(0, 0, 25)
|
||||
self.lightpivot.hprInterval(10, LPoint3(360, 0, 0)).loop()
|
||||
plight = PointLight('plight')
|
||||
plight.setColor((1, 1, 1, 1))
|
||||
plight.setAttenuation(LVector3(0.7, 0.05, 0))
|
||||
plnp = self.lightpivot.attachNewNode(plight)
|
||||
plnp.setPos(45, 0, 0)
|
||||
self.room.setLight(plnp)
|
||||
|
||||
# Add an ambient light
|
||||
alight = AmbientLight('alight')
|
||||
alight.setColor((0.2, 0.2, 0.2, 1))
|
||||
alnp = render.attachNewNode(alight)
|
||||
self.room.setLight(alnp)
|
||||
|
||||
# Create a sphere to denote the light
|
||||
sphere = loader.loadModel("models/icosphere")
|
||||
sphere.reparentTo(plnp)
|
||||
|
||||
# Tell Panda that it should generate shaders performing per-pixel
|
||||
# lighting for the room.
|
||||
self.room.setShaderAuto()
|
||||
|
||||
self.shaderenable = 1
|
||||
|
||||
def setMouseBtn(self, btn, value):
|
||||
self.mousebtn[btn] = value
|
||||
|
||||
def rotateLight(self, offset):
|
||||
self.lightpivot.setH(self.lightpivot.getH() + offset * 20)
|
||||
|
||||
def rotateCam(self, offset):
|
||||
self.heading = self.heading - offset * 10
|
||||
|
||||
def toggleShader(self):
|
||||
self.inst5.destroy()
|
||||
if (self.shaderenable):
|
||||
self.inst5 = addInstructions(0.30, "Enter: Turn bump maps On")
|
||||
self.shaderenable = 0
|
||||
self.room.setShaderOff()
|
||||
else:
|
||||
self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off")
|
||||
self.shaderenable = 1
|
||||
self.room.setShaderAuto()
|
||||
|
||||
def controlCamera(self, task):
|
||||
# figure out how much the mouse has moved (in pixels)
|
||||
md = self.win.getPointer(0)
|
||||
x = md.getX()
|
||||
y = md.getY()
|
||||
if self.win.movePointer(0, 100, 100):
|
||||
self.heading = self.heading - (x - 100) * 0.2
|
||||
self.pitch = self.pitch - (y - 100) * 0.2
|
||||
if self.pitch < -45:
|
||||
self.pitch = -45
|
||||
if self.pitch > 45:
|
||||
self.pitch = 45
|
||||
self.camera.setHpr(self.heading, self.pitch, 0)
|
||||
dir = self.camera.getMat().getRow3(1)
|
||||
elapsed = task.time - self.last
|
||||
if self.last == 0:
|
||||
elapsed = 0
|
||||
if self.mousebtn[0]:
|
||||
self.focus = self.focus + dir * elapsed * 30
|
||||
if self.mousebtn[1] or self.mousebtn[2]:
|
||||
self.focus = self.focus - dir * elapsed * 30
|
||||
self.camera.setPos(self.focus - (dir * 5))
|
||||
if self.camera.getX() < -59.0:
|
||||
self.camera.setX(-59)
|
||||
if self.camera.getX() > 59.0:
|
||||
self.camera.setX(59)
|
||||
if self.camera.getY() < -59.0:
|
||||
self.camera.setY(-59)
|
||||
if self.camera.getY() > 59.0:
|
||||
self.camera.setY(59)
|
||||
if self.camera.getZ() < 5.0:
|
||||
self.camera.setZ(5)
|
||||
if self.camera.getZ() > 45.0:
|
||||
self.camera.setZ(45)
|
||||
self.focus = self.camera.getPos() + (dir * 5)
|
||||
self.last = task.time
|
||||
return Task.cont
|
||||
|
||||
demo = BumpMapDemo()
|
||||
demo.run()
|
||||
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
|
@ -0,0 +1,497 @@
|
|||
<CoordinateSystem> { Z-Up }
|
||||
|
||||
<Comment> {
|
||||
"egg-trans -F icosphere.egg -o icosphere.egg"
|
||||
}
|
||||
<Group> Icosphere {
|
||||
<VertexPool> Icosphere {
|
||||
<Vertex> 0 {
|
||||
0 0 -1
|
||||
<Normal> { 0 0 -1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.425323 -0.309011 -0.850654
|
||||
<Normal> { 0.425306 -0.309 -0.850642 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.162456 -0.499995 -0.850654
|
||||
<Normal> { -0.16245 -0.499985 -0.850642 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.723607 -0.525725 -0.44722
|
||||
<Normal> { 0.723594 -0.525712 -0.447188 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
0.850648 0 -0.525736
|
||||
<Normal> { 0.850642 0 -0.525712 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
-0.52573 0 -0.850652
|
||||
<Normal> { -0.525712 0 -0.850642 }
|
||||
}
|
||||
<Vertex> 6 {
|
||||
-0.162456 0.499995 -0.850654
|
||||
<Normal> { -0.16245 0.499985 -0.850642 }
|
||||
}
|
||||
<Vertex> 7 {
|
||||
0.425323 0.309011 -0.850654
|
||||
<Normal> { 0.425306 0.309 -0.850642 }
|
||||
}
|
||||
<Vertex> 8 {
|
||||
0.951058 -0.309013 0
|
||||
<Normal> { 0.951048 -0.309 0 }
|
||||
}
|
||||
<Vertex> 9 {
|
||||
-0.276388 -0.850649 -0.44722
|
||||
<Normal> { -0.276376 -0.850642 -0.447218 }
|
||||
}
|
||||
<Vertex> 10 {
|
||||
0.262869 -0.809012 -0.525738
|
||||
<Normal> { 0.262856 -0.808985 -0.525712 }
|
||||
}
|
||||
<Vertex> 11 {
|
||||
0 -1 0
|
||||
<Normal> { 0 -1 0 }
|
||||
}
|
||||
<Vertex> 12 {
|
||||
-0.894426 0 -0.447216
|
||||
<Normal> { -0.894406 0 -0.447188 }
|
||||
}
|
||||
<Vertex> 13 {
|
||||
-0.688189 -0.499997 -0.525736
|
||||
<Normal> { -0.688162 -0.499985 -0.525712 }
|
||||
}
|
||||
<Vertex> 14 {
|
||||
-0.951058 -0.309013 0
|
||||
<Normal> { -0.951048 -0.309 0 }
|
||||
}
|
||||
<Vertex> 15 {
|
||||
-0.276388 0.850649 -0.44722
|
||||
<Normal> { -0.276376 0.850642 -0.447218 }
|
||||
}
|
||||
<Vertex> 16 {
|
||||
-0.688189 0.499997 -0.525736
|
||||
<Normal> { -0.688162 0.499985 -0.525712 }
|
||||
}
|
||||
<Vertex> 17 {
|
||||
-0.587786 0.809017 0
|
||||
<Normal> { -0.587756 0.809015 0 }
|
||||
}
|
||||
<Vertex> 18 {
|
||||
0.723607 0.525725 -0.44722
|
||||
<Normal> { 0.723594 0.525712 -0.447188 }
|
||||
}
|
||||
<Vertex> 19 {
|
||||
0.262869 0.809012 -0.525738
|
||||
<Normal> { 0.262856 0.808985 -0.525712 }
|
||||
}
|
||||
<Vertex> 20 {
|
||||
0.587786 0.809017 0
|
||||
<Normal> { 0.587756 0.809015 0 }
|
||||
}
|
||||
<Vertex> 21 {
|
||||
0.587786 -0.809017 0
|
||||
<Normal> { 0.587756 -0.809015 0 }
|
||||
}
|
||||
<Vertex> 22 {
|
||||
-0.587786 -0.809017 0
|
||||
<Normal> { -0.587756 -0.809015 0 }
|
||||
}
|
||||
<Vertex> 23 {
|
||||
-0.951058 0.309013 0
|
||||
<Normal> { -0.951048 0.309 0 }
|
||||
}
|
||||
<Vertex> 24 {
|
||||
0 1 0
|
||||
<Normal> { 0 1 0 }
|
||||
}
|
||||
<Vertex> 25 {
|
||||
0.951058 0.309013 0
|
||||
<Normal> { 0.951048 0.309 0 }
|
||||
}
|
||||
<Vertex> 26 {
|
||||
0.276388 -0.850649 0.44722
|
||||
<Normal> { 0.276376 -0.850642 0.447218 }
|
||||
}
|
||||
<Vertex> 27 {
|
||||
0.688189 -0.499997 0.525736
|
||||
<Normal> { 0.688162 -0.499985 0.525712 }
|
||||
}
|
||||
<Vertex> 28 {
|
||||
0.162456 -0.499995 0.850654
|
||||
<Normal> { 0.16245 -0.499985 0.850642 }
|
||||
}
|
||||
<Vertex> 29 {
|
||||
-0.723607 -0.525725 0.44722
|
||||
<Normal> { -0.723594 -0.525712 0.447188 }
|
||||
}
|
||||
<Vertex> 30 {
|
||||
-0.262869 -0.809012 0.525738
|
||||
<Normal> { -0.262856 -0.808985 0.525712 }
|
||||
}
|
||||
<Vertex> 31 {
|
||||
-0.425323 -0.309011 0.850654
|
||||
<Normal> { -0.425306 -0.309 0.850642 }
|
||||
}
|
||||
<Vertex> 32 {
|
||||
-0.723607 0.525725 0.44722
|
||||
<Normal> { -0.723594 0.525712 0.447188 }
|
||||
}
|
||||
<Vertex> 33 {
|
||||
-0.850648 0 0.525736
|
||||
<Normal> { -0.850642 0 0.525712 }
|
||||
}
|
||||
<Vertex> 34 {
|
||||
-0.425323 0.309011 0.850654
|
||||
<Normal> { -0.425306 0.309 0.850642 }
|
||||
}
|
||||
<Vertex> 35 {
|
||||
0.276388 0.850649 0.44722
|
||||
<Normal> { 0.276376 0.850642 0.447218 }
|
||||
}
|
||||
<Vertex> 36 {
|
||||
-0.262869 0.809012 0.525738
|
||||
<Normal> { -0.262856 0.808985 0.525712 }
|
||||
}
|
||||
<Vertex> 37 {
|
||||
0.162456 0.499995 0.850654
|
||||
<Normal> { 0.16245 0.499985 0.850642 }
|
||||
}
|
||||
<Vertex> 38 {
|
||||
0.894426 0 0.447216
|
||||
<Normal> { 0.894406 0 0.447188 }
|
||||
}
|
||||
<Vertex> 39 {
|
||||
0.688189 0.499997 0.525736
|
||||
<Normal> { 0.688162 0.499985 0.525712 }
|
||||
}
|
||||
<Vertex> 40 {
|
||||
0.52573 0 0.850652
|
||||
<Normal> { 0.525712 0 0.850642 }
|
||||
}
|
||||
<Vertex> 41 {
|
||||
0 0 1
|
||||
<Normal> { 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.102381 -0.31509 -0.943523 }
|
||||
<VertexRef> { 0 1 2 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.700224 -0.268032 -0.661699 }
|
||||
<VertexRef> { 3 1 4 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.268034 -0.194736 -0.943523 }
|
||||
<VertexRef> { 0 2 5 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.268034 0.194737 -0.943523 }
|
||||
<VertexRef> { 0 5 6 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.102381 0.31509 -0.943523 }
|
||||
<VertexRef> { 0 6 7 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.904989 -0.268032 -0.330385 }
|
||||
<VertexRef> { 3 4 8 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.024747 -0.943521 -0.330386 }
|
||||
<VertexRef> { 9 10 11 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.889697 -0.315095 -0.330385 }
|
||||
<VertexRef> { 12 13 14 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.574602 0.748784 -0.330388 }
|
||||
<VertexRef> { 15 16 17 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.534576 0.777865 -0.330387 }
|
||||
<VertexRef> { 18 19 20 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.802609 -0.583126 -0.125627 }
|
||||
<VertexRef> { 3 8 21 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.306569 -0.943522 -0.125629 }
|
||||
<VertexRef> { 9 11 22 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.992077 0 -0.125628 }
|
||||
<VertexRef> { 12 14 23 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.306569 0.943522 -0.125629 }
|
||||
<VertexRef> { 15 17 24 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.802609 0.583126 -0.125627 }
|
||||
<VertexRef> { 18 20 25 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.408946 -0.628425 0.661698 }
|
||||
<VertexRef> { 26 27 28 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.4713 -0.583122 0.661699 }
|
||||
<VertexRef> { 29 30 31 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.700224 0.268032 0.661699 }
|
||||
<VertexRef> { 32 33 34 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.03853 0.748779 0.661699 }
|
||||
<VertexRef> { 35 36 37 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.724042 0.194736 0.661695 }
|
||||
<VertexRef> { 38 39 40 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.268034 0.194737 0.943523 }
|
||||
<VertexRef> { 40 37 41 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.491119 0.356821 0.794657 }
|
||||
<VertexRef> { 40 39 37 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.408946 0.628425 0.661699 }
|
||||
<VertexRef> { 39 35 37 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.102381 0.31509 0.943523 }
|
||||
<VertexRef> { 37 34 41 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.187594 0.577345 0.794658 }
|
||||
<VertexRef> { 37 36 34 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.4713 0.583122 0.661699 }
|
||||
<VertexRef> { 36 32 34 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.331305 0 0.943524 }
|
||||
<VertexRef> { 34 31 41 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.60706 0 0.794656 }
|
||||
<VertexRef> { 34 33 31 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.700224 -0.268032 0.661699 }
|
||||
<VertexRef> { 33 29 31 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.102381 -0.31509 0.943523 }
|
||||
<VertexRef> { 31 28 41 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.187594 -0.577345 0.794658 }
|
||||
<VertexRef> { 31 30 28 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.03853 -0.748779 0.661699 }
|
||||
<VertexRef> { 30 26 28 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.268034 -0.194737 0.943523 }
|
||||
<VertexRef> { 28 40 41 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.491119 -0.356821 0.794657 }
|
||||
<VertexRef> { 28 27 40 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.724042 -0.194736 0.661695 }
|
||||
<VertexRef> { 27 38 40 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.889697 0.315095 0.330385 }
|
||||
<VertexRef> { 25 39 38 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.794656 0.577348 0.187595 }
|
||||
<VertexRef> { 25 20 39 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.574602 0.748784 0.330388 }
|
||||
<VertexRef> { 20 35 39 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.024747 0.943521 0.330386 }
|
||||
<VertexRef> { 24 36 35 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.303531 0.934171 0.187597 }
|
||||
<VertexRef> { 24 17 36 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.534576 0.777865 0.330387 }
|
||||
<VertexRef> { 17 32 36 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.904989 0.268032 0.330385 }
|
||||
<VertexRef> { 23 33 32 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.982246 0 0.187599 }
|
||||
<VertexRef> { 23 14 33 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.904989 -0.268031 0.330385 }
|
||||
<VertexRef> { 14 29 33 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.534576 -0.777865 0.330387 }
|
||||
<VertexRef> { 22 30 29 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.303531 -0.934171 0.187597 }
|
||||
<VertexRef> { 22 11 30 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.024747 -0.943521 0.330386 }
|
||||
<VertexRef> { 11 26 30 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.574602 -0.748784 0.330388 }
|
||||
<VertexRef> { 21 27 26 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.794656 -0.577348 0.187595 }
|
||||
<VertexRef> { 21 8 27 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.889697 -0.315095 0.330385 }
|
||||
<VertexRef> { 8 38 27 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.306569 0.943522 0.125629 }
|
||||
<VertexRef> { 20 24 35 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.303531 0.934171 -0.187597 }
|
||||
<VertexRef> { 20 19 24 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.024747 0.943521 -0.330386 }
|
||||
<VertexRef> { 19 15 24 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.802609 0.583126 0.125627 }
|
||||
<VertexRef> { 17 23 32 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.794656 0.577348 -0.187595 }
|
||||
<VertexRef> { 17 16 23 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.889697 0.315095 -0.330385 }
|
||||
<VertexRef> { 16 12 23 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.802609 -0.583126 0.125627 }
|
||||
<VertexRef> { 14 22 29 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.794656 -0.577348 -0.187595 }
|
||||
<VertexRef> { 14 13 22 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.574602 -0.748784 -0.330388 }
|
||||
<VertexRef> { 13 9 22 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.306569 -0.943522 0.125629 }
|
||||
<VertexRef> { 11 21 26 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.303531 -0.934171 -0.187597 }
|
||||
<VertexRef> { 11 10 21 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.534576 -0.777865 -0.330387 }
|
||||
<VertexRef> { 10 3 21 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.992077 0 0.125628 }
|
||||
<VertexRef> { 8 25 38 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.982246 0 -0.187599 }
|
||||
<VertexRef> { 8 4 25 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.904989 0.268031 -0.330385 }
|
||||
<VertexRef> { 4 18 25 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.4713 0.583122 -0.661699 }
|
||||
<VertexRef> { 7 19 18 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.187594 0.577345 -0.794658 }
|
||||
<VertexRef> { 7 6 19 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.03853 0.748779 -0.661699 }
|
||||
<VertexRef> { 6 15 19 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.408946 0.628425 -0.661698 }
|
||||
<VertexRef> { 6 16 15 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.491119 0.356821 -0.794657 }
|
||||
<VertexRef> { 6 5 16 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.724042 0.194736 -0.661695 }
|
||||
<VertexRef> { 5 12 16 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.724042 -0.194736 -0.661695 }
|
||||
<VertexRef> { 5 13 12 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.491119 -0.356821 -0.794657 }
|
||||
<VertexRef> { 5 2 13 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.408946 -0.628425 -0.661698 }
|
||||
<VertexRef> { 2 9 13 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.700224 0.268032 -0.661699 }
|
||||
<VertexRef> { 4 7 18 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.60706 0 -0.794656 }
|
||||
<VertexRef> { 4 1 7 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.331305 0 -0.943524 }
|
||||
<VertexRef> { 1 0 7 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { -0.03853 -0.748779 -0.661699 }
|
||||
<VertexRef> { 2 10 9 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.187594 -0.577345 -0.794658 }
|
||||
<VertexRef> { 2 1 10 <Ref> { Icosphere } }
|
||||
}
|
||||
<Polygon> {
|
||||
<Normal> { 0.4713 -0.583122 -0.661699 }
|
||||
<VertexRef> { 1 3 10 <Ref> { Icosphere } }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
|
@ -0,0 +1,196 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Shao Zhang, Phil Saltzman, and Eddie Canaan
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This tutorial will demonstrate some uses for intervals in Panda
|
||||
# to move objects in your panda world.
|
||||
# Intervals are tools that change a value of something, like position,
|
||||
# rotation or anything else, linearly, over a set period of time. They can be
|
||||
# also be combined to work in sequence or in Parallel
|
||||
#
|
||||
# In this lesson, we will simulate a carousel in motion using intervals.
|
||||
# The carousel will spin using an hprInterval while 4 pandas will represent
|
||||
# the horses on a traditional carousel. The 4 pandas will rotate with the
|
||||
# carousel and also move up and down on their poles using a LerpFunc interval.
|
||||
# Finally there will also be lights on the outer edge of the carousel that
|
||||
# will turn on and off by switching their texture with intervals in Sequence
|
||||
# and Parallel
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import AmbientLight, DirectionalLight, LightAttrib
|
||||
from panda3d.core import NodePath
|
||||
from panda3d.core import LVector3
|
||||
from direct.interval.IntervalGlobal import * # Needed to use Intervals
|
||||
from direct.gui.DirectGui import *
|
||||
|
||||
# Importing math constants and functions
|
||||
from math import pi, sin
|
||||
|
||||
|
||||
class CarouselDemo(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# This creates the on screen title that is in every tutorial
|
||||
self.title = OnscreenText(text="Panda3D: Tutorial - Carousel",
|
||||
parent=base.a2dBottomCenter,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5),
|
||||
pos=(0, .1), scale=.1)
|
||||
|
||||
base.disableMouse() # Allow manual positioning of the camera
|
||||
camera.setPosHpr(0, -8, 2.5, 0, -9, 0) # Set the cameras' position
|
||||
# and orientation
|
||||
|
||||
self.loadModels() # Load and position our models
|
||||
self.setupLights() # Add some basic lighting
|
||||
self.startCarousel() # Create the needed intervals and put the
|
||||
# carousel into motion
|
||||
|
||||
def loadModels(self):
|
||||
# Load the carousel base
|
||||
self.carousel = loader.loadModel("models/carousel_base")
|
||||
self.carousel.reparentTo(render) # Attach it to render
|
||||
|
||||
# Load the modeled lights that are on the outer rim of the carousel
|
||||
# (not Panda lights)
|
||||
# There are 2 groups of lights. At any given time, one group will have
|
||||
# the "on" texture and the other will have the "off" texture.
|
||||
self.lights1 = loader.loadModel("models/carousel_lights")
|
||||
self.lights1.reparentTo(self.carousel)
|
||||
|
||||
# Load the 2nd set of lights
|
||||
self.lights2 = loader.loadModel("models/carousel_lights")
|
||||
# We need to rotate the 2nd so it doesn't overlap with the 1st set.
|
||||
self.lights2.setH(36)
|
||||
self.lights2.reparentTo(self.carousel)
|
||||
|
||||
# Load the textures for the lights. One texture is for the "on" state,
|
||||
# the other is for the "off" state.
|
||||
self.lightOffTex = loader.loadTexture("models/carousel_lights_off.jpg")
|
||||
self.lightOnTex = loader.loadTexture("models/carousel_lights_on.jpg")
|
||||
|
||||
# Create an list (self.pandas) with filled with 4 dummy nodes attached
|
||||
# to the carousel.
|
||||
# This uses a python concept called "Array Comprehensions." Check the
|
||||
# Python manual for more information on how they work
|
||||
self.pandas = [self.carousel.attachNewNode("panda" + str(i))
|
||||
for i in range(4)]
|
||||
self.models = [loader.loadModel("models/carousel_panda")
|
||||
for i in range(4)]
|
||||
self.moves = [0] * 4
|
||||
|
||||
for i in range(4):
|
||||
# set the position and orientation of the ith panda node we just created
|
||||
# The Z value of the position will be the base height of the pandas.
|
||||
# The headings are multiplied by i to put each panda in its own position
|
||||
# around the carousel
|
||||
self.pandas[i].setPosHpr(0, 0, 1.3, i * 90, 0, 0)
|
||||
|
||||
# Load the actual panda model, and parent it to its dummy node
|
||||
self.models[i].reparentTo(self.pandas[i])
|
||||
# Set the distance from the center. This distance is based on the way the
|
||||
# carousel was modeled in Maya
|
||||
self.models[i].setY(.85)
|
||||
|
||||
# Load the environment (Sky sphere and ground plane)
|
||||
self.env = loader.loadModel("models/env")
|
||||
self.env.reparentTo(render)
|
||||
self.env.setScale(7)
|
||||
|
||||
# Panda Lighting
|
||||
def setupLights(self):
|
||||
# Create some lights and add them to the scene. By setting the lights on
|
||||
# render they affect the entire scene
|
||||
# Check out the lighting tutorial for more information on lights
|
||||
ambientLight = AmbientLight("ambientLight")
|
||||
ambientLight.setColor((.4, .4, .35, 1))
|
||||
directionalLight = DirectionalLight("directionalLight")
|
||||
directionalLight.setDirection(LVector3(0, 8, -2.5))
|
||||
directionalLight.setColor((0.9, 0.8, 0.9, 1))
|
||||
render.setLight(render.attachNewNode(directionalLight))
|
||||
render.setLight(render.attachNewNode(ambientLight))
|
||||
|
||||
# Explicitly set the environment to not be lit
|
||||
self.env.setLightOff()
|
||||
|
||||
def startCarousel(self):
|
||||
# Here's where we actually create the intervals to move the carousel
|
||||
# The first type of interval we use is one created directly from a NodePath
|
||||
# This interval tells the NodePath to vary its orientation (hpr) from its
|
||||
# current value (0,0,0) to (360,0,0) over 20 seconds. Intervals created from
|
||||
# NodePaths also exist for position, scale, color, and shear
|
||||
|
||||
self.carouselSpin = self.carousel.hprInterval(20, LVector3(360, 0, 0))
|
||||
# Once an interval is created, we need to tell it to actually move.
|
||||
# start() will cause an interval to play once. loop() will tell an interval
|
||||
# to repeat once it finished. To keep the carousel turning, we use
|
||||
# loop()
|
||||
self.carouselSpin.loop()
|
||||
|
||||
# The next type of interval we use is called a LerpFunc interval. It is
|
||||
# called that becuase it linearly interpolates (aka Lerp) values passed to
|
||||
# a function over a given amount of time.
|
||||
|
||||
# In this specific case, horses on a carousel don't move contantly up,
|
||||
# suddenly stop, and then contantly move down again. Instead, they start
|
||||
# slowly, get fast in the middle, and slow down at the top. This motion is
|
||||
# close to a sine wave. This LerpFunc calls the function oscillatePanda
|
||||
# (which we will create below), which changes the height of the panda based
|
||||
# on the sin of the value passed in. In this way we achieve non-linear
|
||||
# motion by linearly changing the input to a function
|
||||
for i in range(4):
|
||||
self.moves[i] = LerpFunc(
|
||||
self.oscillatePanda, # function to call
|
||||
duration=3, # 3 second duration
|
||||
fromData=0, # starting value (in radians)
|
||||
toData=2 * pi, # ending value (2pi radians = 360 degrees)
|
||||
# Additional information to pass to
|
||||
# self.oscialtePanda
|
||||
extraArgs=[self.models[i], pi * (i % 2)]
|
||||
)
|
||||
# again, we want these to play continuously so we start them with
|
||||
# loop()
|
||||
self.moves[i].loop()
|
||||
|
||||
# Finally, we combine Sequence, Parallel, Func, and Wait intervals,
|
||||
# to schedule texture swapping on the lights to simulate the lights turning
|
||||
# on and off.
|
||||
# Sequence intervals play other intervals in a sequence. In other words,
|
||||
# it waits for the current interval to finish before playing the next
|
||||
# one.
|
||||
# Parallel intervals play a group of intervals at the same time
|
||||
# Wait intervals simply do nothing for a given amount of time
|
||||
# Func intervals simply make a single function call. This is helpful because
|
||||
# it allows us to schedule functions to be called in a larger sequence. They
|
||||
# take virtually no time so they don't cause a Sequence to wait.
|
||||
|
||||
self.lightBlink = Sequence(
|
||||
# For the first step in our sequence we will set the on texture on one
|
||||
# light and set the off texture on the other light at the same time
|
||||
Parallel(
|
||||
Func(self.lights1.setTexture, self.lightOnTex, 1),
|
||||
Func(self.lights2.setTexture, self.lightOffTex, 1)),
|
||||
Wait(1), # Then we will wait 1 second
|
||||
# Then we will switch the textures at the same time
|
||||
Parallel(
|
||||
Func(self.lights1.setTexture, self.lightOffTex, 1),
|
||||
Func(self.lights2.setTexture, self.lightOnTex, 1)),
|
||||
Wait(1) # Then we will wait another second
|
||||
)
|
||||
|
||||
self.lightBlink.loop() # Loop this sequence continuously
|
||||
|
||||
def oscillatePanda(self, rad, panda, offset):
|
||||
# This is the oscillation function mentioned earlier. It takes in a
|
||||
# degree value, a NodePath to set the height on, and an offset. The
|
||||
# offset is there so that the different pandas can move opposite to
|
||||
# each other. The .2 is the amplitude, so the height of the panda will
|
||||
# vary from -.2 to .2
|
||||
panda.setZ(sin(rad + offset) * .2)
|
||||
|
||||
demo = CarouselDemo()
|
||||
demo.run()
|
||||
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Kwasi Mensah
|
||||
# Date: 7/11/2005
|
||||
#
|
||||
# This is a tutorial to show some of the more advanced things
|
||||
# you can do with Cg. Specifically, with Non Photo Realistic
|
||||
# effects like Toon Shading. It also shows how to implement
|
||||
# multiple buffers in Panda.
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import PandaNode, LightNode, TextNode
|
||||
from panda3d.core import Filename
|
||||
from panda3d.core import NodePath
|
||||
from panda3d.core import Shader
|
||||
from panda3d.core import LVecBase4
|
||||
from direct.task.Task import Task
|
||||
from direct.actor.Actor import Actor
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.showbase.BufferViewer import BufferViewer
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
# Function to put instructions on the screen.
|
||||
def addInstructions(pos, msg):
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.08, -pos - 0.04), scale=.05)
|
||||
|
||||
# Function to put title on the screen.
|
||||
def addTitle(text):
|
||||
return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class ToonMaker(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
self.disableMouse()
|
||||
camera.setPos(0, -50, 0)
|
||||
|
||||
# Check video card capabilities.
|
||||
if not self.win.getGsg().getSupportsBasicShaders():
|
||||
addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.")
|
||||
return
|
||||
|
||||
# Show instructions in the corner of the window.
|
||||
self.title = addTitle(
|
||||
"Panda3D: Tutorial - Toon Shading with Normals-Based Inking")
|
||||
self.inst1 = addInstructions(0.06, "ESC: Quit")
|
||||
self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness")
|
||||
self.inst3 = addInstructions(0.18, "Left/Right: Decrease/Increase Line Darkness")
|
||||
self.inst4 = addInstructions(0.24, "V: View the render-to-texture results")
|
||||
|
||||
# This shader's job is to render the model with discrete lighting
|
||||
# levels. The lighting calculations built into the shader assume
|
||||
# a single nonattenuating point light.
|
||||
|
||||
tempnode = NodePath(PandaNode("temp node"))
|
||||
tempnode.setShader(loader.loadShader("lightingGen.sha"))
|
||||
self.cam.node().setInitialState(tempnode.getState())
|
||||
|
||||
# This is the object that represents the single "light", as far
|
||||
# the shader is concerned. It's not a real Panda3D LightNode, but
|
||||
# the shader doesn't care about that.
|
||||
|
||||
light = render.attachNewNode("light")
|
||||
light.setPos(30, -50, 0)
|
||||
|
||||
# this call puts the light's nodepath into the render state.
|
||||
# this enables the shader to access this light by name.
|
||||
|
||||
render.setShaderInput("light", light)
|
||||
|
||||
# The "normals buffer" will contain a picture of the model colorized
|
||||
# so that the color of the model is a representation of the model's
|
||||
# normal at that point.
|
||||
|
||||
normalsBuffer = self.win.makeTextureBuffer("normalsBuffer", 0, 0)
|
||||
normalsBuffer.setClearColor(LVecBase4(0.5, 0.5, 0.5, 1))
|
||||
self.normalsBuffer = normalsBuffer
|
||||
normalsCamera = self.makeCamera(
|
||||
normalsBuffer, lens=self.cam.node().getLens())
|
||||
normalsCamera.node().setScene(render)
|
||||
tempnode = NodePath(PandaNode("temp node"))
|
||||
tempnode.setShader(loader.loadShader("normalGen.sha"))
|
||||
normalsCamera.node().setInitialState(tempnode.getState())
|
||||
|
||||
# what we actually do to put edges on screen is apply them as a texture to
|
||||
# a transparent screen-fitted card
|
||||
|
||||
drawnScene = normalsBuffer.getTextureCard()
|
||||
drawnScene.setTransparency(1)
|
||||
drawnScene.setColor(1, 1, 1, 0)
|
||||
drawnScene.reparentTo(render2d)
|
||||
self.drawnScene = drawnScene
|
||||
|
||||
# this shader accepts, as input, the picture from the normals buffer.
|
||||
# it compares each adjacent pixel, looking for discontinuities.
|
||||
# wherever a discontinuity exists, it emits black ink.
|
||||
|
||||
self.separation = 0.001
|
||||
self.cutoff = 0.3
|
||||
inkGen = loader.loadShader("inkGen.sha")
|
||||
drawnScene.setShader(inkGen)
|
||||
drawnScene.setShaderInput("separation", LVecBase4(self.separation, 0, self.separation, 0))
|
||||
drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
|
||||
|
||||
# Panda contains a built-in viewer that lets you view the results of
|
||||
# your render-to-texture operations. This code configures the viewer.
|
||||
|
||||
self.accept("v", self.bufferViewer.toggleEnable)
|
||||
self.accept("V", self.bufferViewer.toggleEnable)
|
||||
self.bufferViewer.setPosition("llcorner")
|
||||
|
||||
# Load a dragon model and start its animation.
|
||||
self.character = Actor()
|
||||
self.character.loadModel('models/nik-dragon')
|
||||
self.character.reparentTo(render)
|
||||
self.character.loop('win')
|
||||
self.character.hprInterval(15, (360, 0, 0)).loop()
|
||||
|
||||
# These allow you to change cartooning parameters in realtime
|
||||
self.accept("escape", sys.exit, [0])
|
||||
self.accept("arrow_up", self.increaseSeparation)
|
||||
self.accept("arrow_down", self.decreaseSeparation)
|
||||
self.accept("arrow_left", self.increaseCutoff)
|
||||
self.accept("arrow_right", self.decreaseCutoff)
|
||||
|
||||
def increaseSeparation(self):
|
||||
self.separation = self.separation * 1.11111111
|
||||
print("separation: %f" % (self.separation))
|
||||
self.drawnScene.setShaderInput(
|
||||
"separation", LVecBase4(self.separation, 0, self.separation, 0))
|
||||
|
||||
def decreaseSeparation(self):
|
||||
self.separation = self.separation * 0.90000000
|
||||
print("separation: %f" % (self.separation))
|
||||
self.drawnScene.setShaderInput(
|
||||
"separation", LVecBase4(self.separation, 0, self.separation, 0))
|
||||
|
||||
def increaseCutoff(self):
|
||||
self.cutoff = self.cutoff * 1.11111111
|
||||
print("cutoff: %f" % (self.cutoff))
|
||||
self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
|
||||
|
||||
def decreaseCutoff(self):
|
||||
self.cutoff = self.cutoff * 0.90000000
|
||||
print("cutoff: %f" % (self.cutoff))
|
||||
self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
|
||||
|
||||
t = ToonMaker()
|
||||
t.run()
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import PandaNode, LightNode, TextNode
|
||||
from panda3d.core import Filename, NodePath
|
||||
from panda3d.core import PointLight, AmbientLight
|
||||
from panda3d.core import LightRampAttrib, AuxBitplaneAttrib
|
||||
from panda3d.core import CardMaker
|
||||
from panda3d.core import Shader, Texture
|
||||
from direct.task.Task import Task
|
||||
from direct.actor.Actor import Actor
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.showbase.BufferViewer import BufferViewer
|
||||
from direct.filter.CommonFilters import CommonFilters
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
# Function to put instructions on the screen.
|
||||
def addInstructions(pos, msg):
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.08, -pos - 0.04), scale=.05)
|
||||
|
||||
# Function to put title on the screen.
|
||||
def addTitle(text):
|
||||
return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class ToonMaker(ShowBase):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
self.disableMouse()
|
||||
self.cam.node().getLens().setNear(10.0)
|
||||
self.cam.node().getLens().setFar(200.0)
|
||||
camera.setPos(0, -50, 0)
|
||||
|
||||
# Check video card capabilities.
|
||||
if not self.win.getGsg().getSupportsBasicShaders():
|
||||
addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.")
|
||||
return
|
||||
|
||||
# Enable a 'light ramp' - this discretizes the lighting,
|
||||
# which is half of what makes a model look like a cartoon.
|
||||
# Light ramps only work if shader generation is enabled,
|
||||
# so we call 'setShaderAuto'.
|
||||
|
||||
tempnode = NodePath(PandaNode("temp node"))
|
||||
tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4))
|
||||
tempnode.setShaderAuto()
|
||||
self.cam.node().setInitialState(tempnode.getState())
|
||||
|
||||
# Use class 'CommonFilters' to enable a cartoon inking filter.
|
||||
# This can fail if the video card is not powerful enough, if so,
|
||||
# display an error and exit.
|
||||
|
||||
self.separation = 1 # Pixels
|
||||
self.filters = CommonFilters(self.win, self.cam)
|
||||
filterok = self.filters.setCartoonInk(separation=self.separation)
|
||||
if (filterok == False):
|
||||
addTitle(
|
||||
"Toon Shader: Video card not powerful enough to do image postprocessing")
|
||||
return
|
||||
|
||||
# Show instructions in the corner of the window.
|
||||
self.title = addTitle(
|
||||
"Panda3D: Tutorial - Toon Shading with Normals-Based Inking")
|
||||
self.inst1 = addInstructions(0.06, "ESC: Quit")
|
||||
self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness")
|
||||
self.inst3 = addInstructions(0.18, "V: View the render-to-texture results")
|
||||
|
||||
# Load a dragon model and animate it.
|
||||
self.character = Actor()
|
||||
self.character.loadModel('models/nik-dragon')
|
||||
self.character.reparentTo(render)
|
||||
self.character.loadAnims({'win': 'models/nik-dragon'})
|
||||
self.character.loop('win')
|
||||
self.character.hprInterval(15, (360, 0, 0)).loop()
|
||||
|
||||
# Create a non-attenuating point light and an ambient light.
|
||||
plightnode = PointLight("point light")
|
||||
plightnode.setAttenuation((1, 0, 0))
|
||||
plight = render.attachNewNode(plightnode)
|
||||
plight.setPos(30, -50, 0)
|
||||
alightnode = AmbientLight("ambient light")
|
||||
alightnode.setColor((0.8, 0.8, 0.8, 1))
|
||||
alight = render.attachNewNode(alightnode)
|
||||
render.setLight(alight)
|
||||
render.setLight(plight)
|
||||
|
||||
# Panda contains a built-in viewer that lets you view the
|
||||
# results of all render-to-texture operations. This lets you
|
||||
# see what class CommonFilters is doing behind the scenes.
|
||||
self.accept("v", self.bufferViewer.toggleEnable)
|
||||
self.accept("V", self.bufferViewer.toggleEnable)
|
||||
self.bufferViewer.setPosition("llcorner")
|
||||
self.accept("s", self.filters.manager.resizeBuffers)
|
||||
|
||||
# These allow you to change cartooning parameters in realtime
|
||||
self.accept("escape", sys.exit, [0])
|
||||
self.accept("arrow_up", self.increaseSeparation)
|
||||
self.accept("arrow_down", self.decreaseSeparation)
|
||||
|
||||
def increaseSeparation(self):
|
||||
self.separation = self.separation * 1.11111111
|
||||
print("separation: %f" % (self.separation))
|
||||
self.filters.setCartoonInk(separation=self.separation)
|
||||
|
||||
def decreaseSeparation(self):
|
||||
self.separation = self.separation * 0.90000000
|
||||
print("separation: %f" % (self.separation))
|
||||
self.filters.setCartoonInk(separation=self.separation)
|
||||
|
||||
t = ToonMaker()
|
||||
t.run()
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//Cg
|
||||
//
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float4 vtx_texcoord0 : TEXCOORD0,
|
||||
out float4 l_position : POSITION,
|
||||
out float4 l_texcoord0 : TEXCOORD0,
|
||||
uniform float4x4 mat_modelproj)
|
||||
{
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_texcoord0 = vtx_texcoord0;
|
||||
}
|
||||
|
||||
void fshader(float4 l_texcoord0 : TEXCOORD0,
|
||||
uniform sampler2D tex_0 : TEXUNIT0,
|
||||
uniform float4 k_cutoff : C6,
|
||||
uniform float4 k_separation : C7,
|
||||
out float4 o_color : COLOR)
|
||||
{
|
||||
float4 texcoord0 = l_texcoord0 + k_separation.xyzw;
|
||||
float4 color0=tex2D(tex_0, float2(texcoord0.x, texcoord0.y));
|
||||
float4 texcoord1 = l_texcoord0 - k_separation.xyzw;
|
||||
float4 color1=tex2D(tex_0, float2(texcoord1.x, texcoord1.y));
|
||||
float4 texcoord2 = l_texcoord0 + k_separation.wzyx;
|
||||
float4 color2=tex2D(tex_0, float2(texcoord2.x, texcoord2.y));
|
||||
float4 texcoord3 = l_texcoord0 - k_separation.wzyx;
|
||||
float4 color3=tex2D(tex_0, float2(texcoord3.x, texcoord3.y));
|
||||
float4 mx = max(color0,max(color1,max(color2,color3)));
|
||||
float4 mn = min(color0,min(color1,min(color2,color3)));
|
||||
float4 trigger = saturate(((mx-mn) * 3) - k_cutoff.x);
|
||||
float thresh = dot(float3(trigger.x, trigger.y, trigger.z),float3(1,1,1));
|
||||
float4 output_color = float4 (0, 0, 0, thresh);
|
||||
o_color = output_color;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
//Cg
|
||||
//
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float3 vtx_normal : NORMAL,
|
||||
float4 vtx_color : COLOR,
|
||||
out float4 l_position : POSITION,
|
||||
out float4 l_brite : TEXCOORD0,
|
||||
out float4 l_color : COLOR,
|
||||
uniform float4 mspos_light,
|
||||
uniform float4x4 mat_modelproj)
|
||||
{
|
||||
l_position = mul(mat_modelproj, vtx_position);
|
||||
float3 N = normalize(vtx_normal);
|
||||
float3 lightVector = normalize(mspos_light - vtx_position);
|
||||
l_brite = max(dot(N,lightVector), 0);
|
||||
l_color = vtx_color;
|
||||
}
|
||||
|
||||
|
||||
void fshader(float4 l_brite : TEXCOORD0,
|
||||
float4 l_color : COLOR,
|
||||
out float4 o_color : COLOR)
|
||||
{
|
||||
if (l_brite.x<0.5) l_brite=0.8;
|
||||
else l_brite=1.2;
|
||||
o_color=l_brite * l_color;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
//Cg
|
||||
//
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float4 vtx_normal : NORMAL,
|
||||
out float4 l_position : POSITION,
|
||||
out float3 l_color : TEXCOORD0,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 itp_modelview)
|
||||
{
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_color=(float3)mul(itp_modelview, vtx_normal);
|
||||
}
|
||||
|
||||
void fshader(float3 l_color: TEXCOORD0,
|
||||
out float4 o_color: COLOR)
|
||||
{
|
||||
l_color = normalize(l_color);
|
||||
l_color = l_color/2;
|
||||
o_color.rgb = l_color + float4(0.5, 0.5, 0.5, 0.5);
|
||||
o_color.a = 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Shao Zhang and Phil Saltzman
|
||||
# Models: Eddie Canaan
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This tutorial shows how to determine what objects the mouse is pointing to
|
||||
# We do this using a collision ray that extends from the mouse position
|
||||
# and points straight into the scene, and see what it collides with. We pick
|
||||
# the object with the closest collision
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import CollisionTraverser, CollisionNode
|
||||
from panda3d.core import CollisionHandlerQueue, CollisionRay
|
||||
from panda3d.core import AmbientLight, DirectionalLight, LightAttrib
|
||||
from panda3d.core import TextNode
|
||||
from panda3d.core import LPoint3, LVector3, BitMask32
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.task.Task import Task
|
||||
import sys
|
||||
|
||||
# First we define some contants for the colors
|
||||
BLACK = (0, 0, 0, 1)
|
||||
WHITE = (1, 1, 1, 1)
|
||||
HIGHLIGHT = (0, 1, 1, 1)
|
||||
PIECEBLACK = (.15, .15, .15, 1)
|
||||
|
||||
# Now we define some helper functions that we will need later
|
||||
|
||||
# This function, given a line (vector plus origin point) and a desired z value,
|
||||
# will give us the point on the line where the desired z value is what we want.
|
||||
# This is how we know where to position an object in 3D space based on a 2D mouse
|
||||
# position. It also assumes that we are dragging in the XY plane.
|
||||
#
|
||||
# This is derived from the mathmatical of a plane, solved for a given point
|
||||
def PointAtZ(z, point, vec):
|
||||
return point + vec * ((z - point.getZ()) / vec.getZ())
|
||||
|
||||
# A handy little function for getting the proper position for a given square1
|
||||
def SquarePos(i):
|
||||
return LPoint3((i % 8) - 3.5, int(i / 8) - 3.5, 0)
|
||||
|
||||
# Helper function for determining wheter a square should be white or black
|
||||
# The modulo operations (%) generate the every-other pattern of a chess-board
|
||||
def SquareColor(i):
|
||||
if (i + ((i / 8) % 2)) % 2:
|
||||
return BLACK
|
||||
else:
|
||||
return WHITE
|
||||
|
||||
|
||||
class ChessboardDemo(ShowBase):
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# This code puts the standard title and instruction text on screen
|
||||
self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking",
|
||||
style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
|
||||
pos=(0.8, -0.95), scale = .07)
|
||||
self.escapeEvent = OnscreenText(
|
||||
text="ESC: Quit", parent=base.a2dTopLeft,
|
||||
style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.1),
|
||||
align=TextNode.ALeft, scale = .05)
|
||||
self.mouse1Event = OnscreenText(
|
||||
text="Left-click and drag: Pick up and drag piece",
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.16), scale=.05)
|
||||
|
||||
self.accept('escape', sys.exit) # Escape quits
|
||||
self.disableMouse() # Disble mouse camera control
|
||||
camera.setPosHpr(0, -12, 8, 0, -35, 0) # Set the camera
|
||||
self.setupLights() # Setup default lighting
|
||||
|
||||
# Since we are using collision detection to do picking, we set it up like
|
||||
# any other collision detection system with a traverser and a handler
|
||||
self.picker = CollisionTraverser() # Make a traverser
|
||||
self.pq = CollisionHandlerQueue() # Make a handler
|
||||
# Make a collision node for our picker ray
|
||||
self.pickerNode = CollisionNode('mouseRay')
|
||||
# Attach that node to the camera since the ray will need to be positioned
|
||||
# relative to it
|
||||
self.pickerNP = camera.attachNewNode(self.pickerNode)
|
||||
# Everything to be picked will use bit 1. This way if we were doing other
|
||||
# collision we could seperate it
|
||||
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
|
||||
self.pickerRay = CollisionRay() # Make our ray
|
||||
# Add it to the collision node
|
||||
self.pickerNode.addSolid(self.pickerRay)
|
||||
# Register the ray as something that can cause collisions
|
||||
self.picker.addCollider(self.pickerNP, self.pq)
|
||||
# self.picker.showCollisions(render)
|
||||
|
||||
# Now we create the chess board and its pieces
|
||||
|
||||
# We will attach all of the squares to their own root. This way we can do the
|
||||
# collision pass just on the sqaures and save the time of checking the rest
|
||||
# of the scene
|
||||
self.squareRoot = render.attachNewNode("squareRoot")
|
||||
|
||||
# For each square
|
||||
self.squares = [None for i in range(64)]
|
||||
self.pieces = [None for i in range(64)]
|
||||
for i in range(64):
|
||||
# Load, parent, color, and position the model (a single square
|
||||
# polygon)
|
||||
self.squares[i] = loader.loadModel("models/square")
|
||||
self.squares[i].reparentTo(self.squareRoot)
|
||||
self.squares[i].setPos(SquarePos(i))
|
||||
self.squares[i].setColor(SquareColor(i))
|
||||
# Set the model itself to be collideable with the ray. If this model was
|
||||
# any more complex than a single polygon, you should set up a collision
|
||||
# sphere around it instead. But for single polygons this works
|
||||
# fine.
|
||||
self.squares[i].find("**/polygon").node().setIntoCollideMask(
|
||||
BitMask32.bit(1))
|
||||
# Set a tag on the square's node so we can look up what square this is
|
||||
# later during the collision pass
|
||||
self.squares[i].find("**/polygon").node().setTag('square', str(i))
|
||||
|
||||
# We will use this variable as a pointer to whatever piece is currently
|
||||
# in this square
|
||||
|
||||
# The order of pieces on a chessboard from white's perspective. This list
|
||||
# contains the constructor functions for the piece classes defined
|
||||
# below
|
||||
pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook)
|
||||
|
||||
for i in range(8, 16):
|
||||
# Load the white pawns
|
||||
self.pieces[i] = Pawn(i, WHITE)
|
||||
for i in range(48, 56):
|
||||
# load the black pawns
|
||||
self.pieces[i] = Pawn(i, PIECEBLACK)
|
||||
for i in range(8):
|
||||
# Load the special pieces for the front row and color them white
|
||||
self.pieces[i] = pieceOrder[i](i, WHITE)
|
||||
# Load the special pieces for the back row and color them black
|
||||
self.pieces[i + 56] = pieceOrder[i](i + 56, PIECEBLACK)
|
||||
|
||||
# This will represent the index of the currently highlited square
|
||||
self.hiSq = False
|
||||
# This wil represent the index of the square where currently dragged piece
|
||||
# was grabbed from
|
||||
self.dragging = False
|
||||
|
||||
# Start the task that handles the picking
|
||||
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
|
||||
self.accept("mouse1", self.grabPiece) # left-click grabs a piece
|
||||
self.accept("mouse1-up", self.releasePiece) # releasing places it
|
||||
|
||||
# This function swaps the positions of two pieces
|
||||
def swapPieces(self, fr, to):
|
||||
temp = self.pieces[fr]
|
||||
self.pieces[fr] = self.pieces[to]
|
||||
self.pieces[to] = temp
|
||||
if self.pieces[fr]:
|
||||
self.pieces[fr].square = fr
|
||||
self.pieces[fr].obj.setPos(SquarePos(fr))
|
||||
if self.pieces[to]:
|
||||
self.pieces[to].square = to
|
||||
self.pieces[to].obj.setPos(SquarePos(to))
|
||||
|
||||
def mouseTask(self, task):
|
||||
# This task deals with the highlighting and dragging based on the mouse
|
||||
|
||||
# First, clear the current highlight
|
||||
if self.hiSq is not False:
|
||||
self.squares[self.hiSq].setColor(SquareColor(self.hiSq))
|
||||
self.hiSq = False
|
||||
|
||||
# Check to see if we can access the mouse. We need it to do anything
|
||||
# else
|
||||
if self.mouseWatcherNode.hasMouse():
|
||||
# get the mouse position
|
||||
mpos = self.mouseWatcherNode.getMouse()
|
||||
|
||||
# Set the position of the ray based on the mouse position
|
||||
self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())
|
||||
|
||||
# If we are dragging something, set the position of the object
|
||||
# to be at the appropriate point over the plane of the board
|
||||
if self.dragging is not False:
|
||||
# Gets the point described by pickerRay.getOrigin(), which is relative to
|
||||
# camera, relative instead to render
|
||||
nearPoint = render.getRelativePoint(
|
||||
camera, self.pickerRay.getOrigin())
|
||||
# Same thing with the direction of the ray
|
||||
nearVec = render.getRelativeVector(
|
||||
camera, self.pickerRay.getDirection())
|
||||
self.pieces[self.dragging].obj.setPos(
|
||||
PointAtZ(.5, nearPoint, nearVec))
|
||||
|
||||
# Do the actual collision pass (Do it only on the squares for
|
||||
# efficiency purposes)
|
||||
self.picker.traverse(self.squareRoot)
|
||||
if self.pq.getNumEntries() > 0:
|
||||
# if we have hit something, sort the hits so that the closest
|
||||
# is first, and highlight that node
|
||||
self.pq.sortEntries()
|
||||
i = int(self.pq.getEntry(0).getIntoNode().getTag('square'))
|
||||
# Set the highlight on the picked square
|
||||
self.squares[i].setColor(HIGHLIGHT)
|
||||
self.hiSq = i
|
||||
|
||||
return Task.cont
|
||||
|
||||
def grabPiece(self):
|
||||
# If a square is highlighted and it has a piece, set it to dragging
|
||||
# mode
|
||||
if self.hiSq is not False and self.pieces[self.hiSq]:
|
||||
self.dragging = self.hiSq
|
||||
self.hiSq = False
|
||||
|
||||
def releasePiece(self):
|
||||
# Letting go of a piece. If we are not on a square, return it to its original
|
||||
# position. Otherwise, swap it with the piece in the new square
|
||||
# Make sure we really are dragging something
|
||||
if self.dragging is not False:
|
||||
# We have let go of the piece, but we are not on a square
|
||||
if self.hiSq is False:
|
||||
self.pieces[self.dragging].obj.setPos(
|
||||
SquarePos(self.dragging))
|
||||
else:
|
||||
# Otherwise, swap the pieces
|
||||
self.swapPieces(self.dragging, self.hiSq)
|
||||
|
||||
# We are no longer dragging anything
|
||||
self.dragging = False
|
||||
|
||||
def setupLights(self): # This function sets up some default lighting
|
||||
ambientLight = AmbientLight("ambientLight")
|
||||
ambientLight.setColor((.8, .8, .8, 1))
|
||||
directionalLight = DirectionalLight("directionalLight")
|
||||
directionalLight.setDirection(LVector3(0, 45, -45))
|
||||
directionalLight.setColor((0.2, 0.2, 0.2, 1))
|
||||
render.setLight(render.attachNewNode(directionalLight))
|
||||
render.setLight(render.attachNewNode(ambientLight))
|
||||
|
||||
|
||||
# Class for a piece. This just handels loading the model and setting initial
|
||||
# position and color
|
||||
class Piece(object):
|
||||
def __init__(self, square, color):
|
||||
self.obj = loader.loadModel(self.model)
|
||||
self.obj.reparentTo(render)
|
||||
self.obj.setColor(color)
|
||||
self.obj.setPos(SquarePos(square))
|
||||
|
||||
|
||||
# Classes for each type of chess piece
|
||||
# Obviously, we could have done this by just passing a string to Piece's init.
|
||||
# But if you wanted to make rules for how the pieces move, a good place to start
|
||||
# would be to make an isValidMove(toSquare) method for each piece type
|
||||
# and then check if the destination square is acceptible during ReleasePiece
|
||||
class Pawn(Piece):
|
||||
model = "models/pawn"
|
||||
|
||||
class King(Piece):
|
||||
model = "models/king"
|
||||
|
||||
class Queen(Piece):
|
||||
model = "models/queen"
|
||||
|
||||
class Bishop(Piece):
|
||||
model = "models/bishop"
|
||||
|
||||
class Knight(Piece):
|
||||
model = "models/knight"
|
||||
|
||||
class Rook(Piece):
|
||||
model = "models/rook"
|
||||
|
||||
# Do the main initialization and start 3D rendering
|
||||
demo = ChessboardDemo()
|
||||
demo.run()
|
||||
|
|
@ -0,0 +1,586 @@
|
|||
<CoordinateSystem> { Y-Up }
|
||||
|
||||
<Group> cell1 {
|
||||
<VertexPool> cell1-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 0 -4
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
11.5789 0 -11.5789
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-11.5789 0 -11.5789
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 4
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
4 0 -4
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
11.5789 0 11.5789
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 6 {
|
||||
4 0 4
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 7 {
|
||||
-11.5789 0 11.5789
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 2 3 0 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 5 1 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 6 7 5 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 1 0 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 7 3 2 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 6 5 4 <Ref> { cell1-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 7 6 <Ref> { cell1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell2 {
|
||||
<VertexPool> cell2-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 0 -0.838305
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 0 0.527741
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 0 -0.838305
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 0.527741
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell2-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 1 0 <Ref> { cell2-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell3 {
|
||||
<VertexPool> cell3-ORG {
|
||||
<Vertex> 0 {
|
||||
-1.02178 0 -0.838305
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.243417 0 -1.36315
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-1.02178 0 -1.36315
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.36172 0 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
2.99397 0 -1.36315
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
2.99397 0 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 6 {
|
||||
-3.36172 0 -1.36315
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 7 {
|
||||
0.243417 0 -0.838305
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell3-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 1 3 2 <Ref> { cell3-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 5 1 <Ref> { cell3-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 6 2 3 <Ref> { cell3-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 7 1 0 <Ref> { cell3-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 3 1 <Ref> { cell3-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell4 {
|
||||
<VertexPool> cell4-ORG {
|
||||
<Vertex> 0 {
|
||||
2.99397 0 0.527741
|
||||
<Normal> { 0 0.854764 -0.519017 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
2.04443 1.03879 2.23852
|
||||
<Normal> { 0.158619 0.946967 -0.279453 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
2.99397 1.03879 2.23852
|
||||
<Normal> { 0 0.983872 -0.178875 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
2.04443 1.03879 3.18072
|
||||
<Normal> { 0.309844 0.950787 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
2.99397 1.03879 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
0.519489 2.15079 2.23852
|
||||
<Normal> { 0.320561 0.934229 0.156384 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 6 {
|
||||
0.519489 2.15079 3.18072
|
||||
<Normal> { 0.205355 0.978688 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 7 {
|
||||
-0.423812 2.15079 2.23852
|
||||
<Normal> { 0 0.95348 0.301457 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 8 {
|
||||
-0.423812 2.15079 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 9 {
|
||||
0.519489 3.3704 0.502572
|
||||
<Normal> { 0 0.916633 0.399729 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 10 {
|
||||
-0.423812 3.3704 0.502572
|
||||
<Normal> { 0 0.979823 0.199865 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 11 {
|
||||
0.519489 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 12 {
|
||||
-0.423812 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 13 {
|
||||
2.04443 0 0.527741
|
||||
<Normal> { 0 0.854764 -0.519017 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 2 3 4 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 5 6 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 6 7 8 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 7 9 10 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 10 11 12 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 13 1 0 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 1 3 2 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 1 5 3 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 7 6 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 9 7 <Ref> { cell4-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 9 11 10 <Ref> { cell4-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell5 {
|
||||
<VertexPool> cell5-ORG {
|
||||
<Vertex> 0 {
|
||||
2.99397 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.36172 3.3704 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-3.36172 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
2.99397 3.3704 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell5-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 1 0 <Ref> { cell5-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell6 {
|
||||
<VertexPool> cell6-ORG {
|
||||
<Vertex> 0 {
|
||||
2.99397 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.997627 3.3704 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
2.99397 3.3704 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.997627 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell6-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 1 0 <Ref> { cell6-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell7 {
|
||||
<VertexPool> cell7-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.36172 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.900963 3.3704 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.900963 3.3704 0.115602
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.30748 3.3704 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
-2.30748 3.3704 2.05045
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
-3.36172 3.3704 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell7-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 3 4 <Ref> { cell7-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 1 0 <Ref> { cell7-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 3 0 <Ref> { cell7-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell8 {
|
||||
<VertexPool> cell8-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.30748 3.3704 2.23852
|
||||
<Normal> { -0.430354 0.90266 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.82483 5.34053 3.18072
|
||||
<Normal> { -0.430354 0.90266 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 5.34053 2.23852
|
||||
<Normal> { -0.430354 0.90266 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.30748 3.3704 3.18072
|
||||
<Normal> { -0.430354 0.90266 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell8-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 3 1 0 <Ref> { cell8-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell9 {
|
||||
<VertexPool> cell9-ORG {
|
||||
<Vertex> 0 {
|
||||
1.82483 5.34053 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
2.99397 5.34053 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 5.34053 -2.49425
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.485624 5.34053 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
-0.485624 5.34053 -2.49425
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
2.99397 5.34053 3.18072
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell9-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 2 3 4 <Ref> { cell9-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 1 0 <Ref> { cell9-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 1 3 2 <Ref> { cell9-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell10 {
|
||||
<VertexPool> cell10-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.36172 5.34053 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.485624 5.34053 -2.49425
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.485624 5.34053 -3.42013
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.36149 5.34053 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
-2.36149 5.34053 -2.49425
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
-3.36172 5.34053 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell10-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 3 4 <Ref> { cell10-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 1 0 <Ref> { cell10-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 3 0 <Ref> { cell10-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> cell11 {
|
||||
<VertexPool> cell11-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.36149 5.34053 1.0558
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 0.999 0.999 0.999 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-1.90136 5.34053 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-1.90136 5.34053 1.0558
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.31869 5.34053 0.350091
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 4 {
|
||||
1.31869 5.34053 -2.1928
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 5 {
|
||||
-1.90136 5.34053 -2.16895
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 6 {
|
||||
1.31869 5.34053 -0.718624
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 7 {
|
||||
1.82483 5.34053 0.350091
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 8 {
|
||||
-2.36149 5.34053 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 9 {
|
||||
1.31869 5.34053 2.04979
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
<Vertex> 10 {
|
||||
1.82483 5.34053 -0.718624
|
||||
<Normal> { 0 1 0 }
|
||||
<RGBA> { 1 1 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 1 3 2 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 4 5 6 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 6 2 3 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 7 6 3 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 8 1 0 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 9 3 1 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 5 2 6 <Ref> { cell11-ORG } }
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 10 6 7 <Ref> { cell11-ORG } }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,562 @@
|
|||
<CoordinateSystem> { Y-Up }
|
||||
|
||||
<Group> occluder1 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder1-ORG {
|
||||
<Vertex> 0 {
|
||||
4 7.57015 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 -4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-4 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 7.57015 4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder4 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder4-ORG {
|
||||
<Vertex> 0 {
|
||||
4 2.96946e-016 -0.84965
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 -0.84965
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 0 -4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder4-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder2 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder2-ORG {
|
||||
<Vertex> 0 {
|
||||
4 0 -4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 -4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-4 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 -4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder2-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder3 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder3-ORG {
|
||||
<Vertex> 0 {
|
||||
4 0 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-4 7.57015 4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder3-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder5 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder5-ORG {
|
||||
<Vertex> 0 {
|
||||
4 0 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 7.57015 0.551412
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 -2.78684e-016 0.551412
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder5-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder6 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder6-ORG {
|
||||
<Vertex> 0 {
|
||||
4 1.49418 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 7.57015 4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 1.49418 -4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder6-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder7 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder7-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.99576 2.96946e-016 -0.84965
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.99576 7.57015 -0.84965
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-3.99576 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.99576 0 -4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder7-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder8 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder8-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.99576 0 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.99576 7.57015 4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-3.99576 7.57015 0.551412
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.99576 -2.78684e-016 0.551412
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder8-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder9 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder9-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.99576 1.49418 4
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.99576 7.57015 4
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-3.99576 7.57015 -4
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.99576 1.49418 -4
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder9-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder10 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder10-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 1.49418 -1.13843
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 1.49418 -1.13843
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder10-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder11 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder11-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 0 -1.13843
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-1.01962 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-1.01962 -2.78684e-016 -1.13843
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder11-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder12 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder12-ORG {
|
||||
<Vertex> 0 {
|
||||
0.242712 2.96946e-016 -1.13843
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.242712 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 3.24289 -1.13843
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 0 -1.13843
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder12-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder13 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder13-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 3.19567 0.643347
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 3.19567 0.643347
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder13-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder14 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder14-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 3.19567 4.00073
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.508583 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.508583 3.19567 4.00073
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder14-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder15 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder15-ORG {
|
||||
<Vertex> 0 {
|
||||
1.25869 3.19567 3.98826
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.25869 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 3.19567 -3.99828
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 3.19567 3.98826
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder15-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder16 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder16-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 5.26173 2.21946
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 5.26173 -3.99828
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 5.26173 -3.99828
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 5.26173 2.21946
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder16-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder17 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder17-ORG {
|
||||
<Vertex> 0 {
|
||||
1.78343 5.26173 3.98826
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.78343 5.26173 -3.99828
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 5.26173 -3.99828
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 5.26173 3.98826
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder17-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder18 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder18-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.99383 5.23279 2.0951
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.8032 5.23279 2.0951
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.8032 7.56072 2.0951
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.99383 7.56072 2.0951
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder18-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder19 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder19-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.93576 0.0379318 0.6175
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.94837 0.0379318 0.6175
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.94837 3.21608 0.6175
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.93576 3.21608 0.6175
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder19-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder20 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder20-ORG {
|
||||
<Vertex> 0 {
|
||||
11.5843 -0.215275 11.5843
|
||||
}
|
||||
<Vertex> 1 {
|
||||
11.5843 -0.215275 -11.5843
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-11.5843 -0.215275 -11.5843
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-11.5843 -0.215275 11.5843
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder20-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder21 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder21-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.93576 1.56418 0.5914
|
||||
}
|
||||
<Vertex> 1 {
|
||||
3.94341 1.56418 0.5914
|
||||
}
|
||||
<Vertex> 2 {
|
||||
3.94341 3.26834 0.5914
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.93576 3.26834 0.5914
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder21-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder22 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder22-ORG {
|
||||
<Vertex> 0 {
|
||||
-0.694875 1.49418 2.11153
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.694875 5.26968 2.11153
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.694875 5.26968 0.21275
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.694875 1.49418 0.21275
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder22-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder23 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder23-ORG {
|
||||
<Vertex> 0 {
|
||||
0.756062 1.49418 2.11153
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.756062 5.26968 2.11153
|
||||
}
|
||||
<Vertex> 2 {
|
||||
0.756062 5.26968 0.21275
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.756062 1.49418 0.21275
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder23-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder24 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder24-ORG {
|
||||
<Vertex> 0 {
|
||||
1.60244 5.2309 2.14179
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.60244 7.52263 2.14179
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.60244 7.52263 0.394334
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.60244 5.2309 0.394334
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder24-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder25 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder25-ORG {
|
||||
<Vertex> 0 {
|
||||
1.60244 5.2309 -0.763555
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.60244 7.52263 -0.763555
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.60244 7.52263 -2.42694
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.60244 5.2309 -2.42694
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder25-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder26 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder26-ORG {
|
||||
<Vertex> 0 {
|
||||
1.78944 5.2309 -2.34074
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.78944 7.52263 -2.34074
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.28798 7.52263 -2.34074
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.28798 5.2309 -2.34074
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder26-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder27 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder27-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.15855 5.2309 1.03841
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-2.15855 7.52263 1.03841
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.15855 7.52263 -2.44292
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.15855 5.2309 -2.44292
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder27-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> occluder28 {
|
||||
<Scalar> occluder { 1 }
|
||||
<VertexPool> occluder28-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.98717 1.54945 3.90197
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.98717 1.54945 -1.30625
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.28595 1.54945 -1.30625
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.28595 1.54945 3.90197
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { occluder28-ORG } }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,674 @@
|
|||
<CoordinateSystem> { Y-Up }
|
||||
|
||||
<Group> portal_10to11_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_10to11_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.36149 5.34053 2.04979
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-2.36149 7.09573 2.04979
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.36149 7.09573 1.0558
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.36149 5.34053 1.0558
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_10to11_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_1to2_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_1to2_1-ORG {
|
||||
<Vertex> 0 {
|
||||
4 4.44089e-016 -0.838305
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 1.48611 -0.838305
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 1.48611 0.527741
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 4.44089e-016 0.527741
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_1to2_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_1to2_2 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_1to2_2-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 0 0.527741
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 1.48611 0.527741
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-4 1.48611 -0.838305
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 -0.838305
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_1to2_2-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_2to3_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_2to3_1-ORG {
|
||||
<Vertex> 0 {
|
||||
0.243417 0 -0.838305
|
||||
<Normal> { 0 -5.37589e-011 1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.243412 1.48611 -0.838305
|
||||
<Normal> { 0 -5.37589e-011 1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-1.03253 1.48611 -0.838305
|
||||
<Normal> { 0 -5.37589e-011 1 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-1.02178 0 -0.838305
|
||||
<Normal> { 0 -5.37589e-011 1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_2to3_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_2to4_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_2to4_1-ORG {
|
||||
<Vertex> 0 {
|
||||
2.04443 0 0.527741
|
||||
<Normal> { 0 1.40115e-009 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
2.04443 1.48611 0.527741
|
||||
<Normal> { 0 1.40115e-009 -1 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
2.99397 1.48611 0.527741
|
||||
<Normal> { 0 1.40115e-009 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
2.99397 0 0.527741
|
||||
<Normal> { 0 1.40115e-009 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_2to4_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_4to5_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_4to5_1-ORG {
|
||||
<Vertex> 0 {
|
||||
0.519489 3.3704 0.115602
|
||||
<Normal> { 0 -0.00293795 0.999996 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.519489 4.88145 0.120042
|
||||
<Normal> { 0 -0.00293795 0.999996 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.423826 4.88145 0.120042
|
||||
<Normal> { 0 -0.00293795 0.999996 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.423812 3.3704 0.115602
|
||||
<Normal> { 0 -0.00293795 0.999996 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_4to5_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_5to6_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_5to6_1-ORG {
|
||||
<Vertex> 0 {
|
||||
0.997627 3.3704 0.115602
|
||||
<Normal> { 1.00717e-006 1.20465e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
0.997627 5.12958 0.115602
|
||||
<Normal> { 1.00717e-006 1.20465e-006 -1 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
2.99397 5.12958 0.115606
|
||||
<Normal> { 1.00717e-006 1.20465e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
2.99397 3.3704 0.115602
|
||||
<Normal> { 1.00717e-006 1.20465e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_5to6_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_5to7_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_5to7_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-3.36172 3.3704 0.115602
|
||||
<Normal> { 1.02518e-006 -3.5707e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-3.36172 5.12958 0.115593
|
||||
<Normal> { 1.02518e-006 -3.5707e-006 -1 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.900953 5.12958 0.115599
|
||||
<Normal> { 1.02518e-006 -3.5707e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.900963 3.3704 0.115602
|
||||
<Normal> { 1.02518e-006 -3.5707e-006 -1 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_5to7_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_7to8_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_7to8_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.30748 3.3704 3.18072
|
||||
<Normal> { -1 1.34401e-005 1.02127e-006 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-2.30746 5.12958 3.18072
|
||||
<Normal> { -1 1.34401e-005 1.02127e-006 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.30746 5.12958 2.23852
|
||||
<Normal> { -1 1.34401e-005 1.02127e-006 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.30748 3.3704 2.23852
|
||||
<Normal> { -1 1.34401e-005 1.02127e-006 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_7to8_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_8to9_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_8to9_1-ORG {
|
||||
<Vertex> 0 {
|
||||
1.82483 5.34053 3.18072
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.82483 7.09573 3.18072
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 7.09573 2.23852
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.82483 5.34053 2.23852
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_8to9_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_9to10_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_9to10_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-0.485624 5.34053 -3.42013
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.485624 7.09573 -3.42013
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.485624 7.09573 -2.49425
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.485624 5.34053 -2.49425
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_9to10_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_9to11_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_9to11_1-ORG {
|
||||
<Vertex> 0 {
|
||||
1.82483 5.34053 -0.718624
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.82483 7.09573 -0.718624
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 7.09573 0.350091
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.001 0.001 0.999 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.82483 5.34053 0.350091
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0 0 1 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_9to11_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_11to9_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_11to9_1-ORG {
|
||||
<Vertex> 0 {
|
||||
1.82483 5.34053 0.350091
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.82483 7.09573 0.350091
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 7.09573 -0.718624
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.82483 5.34053 -0.718624
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_11to9_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_10to9_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_10to9_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-0.485624 5.34053 -2.49425
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.485624 7.09573 -2.49425
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-0.485624 7.09573 -3.42013
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-0.485624 5.34053 -3.42013
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_10to9_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_9to8_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_9to8_1-ORG {
|
||||
<Vertex> 0 {
|
||||
1.82483 5.34053 2.23852
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
1.82483 7.09573 2.23852
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
1.82483 7.09573 3.18072
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
1.82483 5.34053 3.18072
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_9to8_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_8to7_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_8to7_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.30748 3.3704 2.23852
|
||||
<Normal> { 1 -1.34401e-005 -1.02127e-006 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-2.30746 5.12958 2.23852
|
||||
<Normal> { 1 -1.34401e-005 -1.02127e-006 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.30746 5.12958 3.18072
|
||||
<Normal> { 1 -1.34401e-005 -1.02127e-006 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.30748 3.3704 3.18072
|
||||
<Normal> { 1 -1.34401e-005 -1.02127e-006 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_8to7_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_7to5_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_7to5_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-0.900963 3.3704 0.115602
|
||||
<Normal> { -1.02518e-006 3.5707e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.900953 5.12958 0.115599
|
||||
<Normal> { -1.02518e-006 3.5707e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-3.36172 5.12958 0.115593
|
||||
<Normal> { -1.02518e-006 3.5707e-006 1 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-3.36172 3.3704 0.115602
|
||||
<Normal> { -1.02518e-006 3.5707e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_7to5_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_6to5_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_6to5_1-ORG {
|
||||
<Vertex> 0 {
|
||||
2.99397 3.3704 0.115602
|
||||
<Normal> { -1.00717e-006 -1.20465e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
2.99397 5.12958 0.115606
|
||||
<Normal> { -1.00717e-006 -1.20465e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
0.997627 5.12958 0.115602
|
||||
<Normal> { -1.00717e-006 -1.20465e-006 1 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.997627 3.3704 0.115602
|
||||
<Normal> { -1.00717e-006 -1.20465e-006 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_6to5_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_5to4_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_5to4_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-0.423812 3.3704 0.115602
|
||||
<Normal> { 0 0.00293795 -0.999996 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-0.423826 4.88145 0.120042
|
||||
<Normal> { 0 0.00293795 -0.999996 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
0.519489 4.88145 0.120042
|
||||
<Normal> { 0 0.00293795 -0.999996 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.519489 3.3704 0.115602
|
||||
<Normal> { 0 0.00293795 -0.999996 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_5to4_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_4to2_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_4to2_1-ORG {
|
||||
<Vertex> 0 {
|
||||
2.99397 0 0.527741
|
||||
<Normal> { 0 -1.40115e-009 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
2.99397 1.48611 0.527741
|
||||
<Normal> { 0 -1.40115e-009 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
2.04443 1.48611 0.527741
|
||||
<Normal> { 0 -1.40115e-009 1 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
2.04443 0 0.527741
|
||||
<Normal> { 0 -1.40115e-009 1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_4to2_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_3to2_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_3to2_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-1.02178 0 -0.838305
|
||||
<Normal> { 0 5.37589e-011 -1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-1.03253 1.48611 -0.838305
|
||||
<Normal> { 0 5.37589e-011 -1 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
0.243412 1.48611 -0.838305
|
||||
<Normal> { 0 5.37589e-011 -1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
0.243417 0 -0.838305
|
||||
<Normal> { 0 5.37589e-011 -1 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_3to2_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_2to1_2 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_2to1_2-ORG {
|
||||
<Vertex> 0 {
|
||||
-4 0 -0.838305
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-4 1.48611 -0.838305
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-4 1.48611 0.527741
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-4 0 0.527741
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_2to1_2-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_2to1_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_2to1_1-ORG {
|
||||
<Vertex> 0 {
|
||||
4 4.44089e-016 0.527741
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
4 1.48611 0.527741
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
4 1.48611 -0.838305
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
4 4.44089e-016 -0.838305
|
||||
<Normal> { -1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_2to1_1-ORG } }
|
||||
}
|
||||
}
|
||||
<Group> portal_11to10_1 {
|
||||
<Scalar> portal { 1 }
|
||||
<VertexPool> portal_11to10_1-ORG {
|
||||
<Vertex> 0 {
|
||||
-2.36149 5.34053 1.0558
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 1 {
|
||||
-2.36149 7.09573 1.0558
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
<Vertex> 2 {
|
||||
-2.36149 7.09573 2.04979
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 0.999 0.001 0.001 1 }
|
||||
}
|
||||
<Vertex> 3 {
|
||||
-2.36149 5.34053 2.04979
|
||||
<Normal> { 1 0 0 }
|
||||
<RGBA> { 1 0 0 1 }
|
||||
}
|
||||
}
|
||||
<Polygon> {
|
||||
<VertexRef> { 0 1 2 3 <Ref> { portal_11to10_1-ORG } }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 189 B |
|
|
@ -0,0 +1,159 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Author: Josh Enes
|
||||
Last Updated: 2015-03-13
|
||||
|
||||
This is a demo of Panda's occluder-culling system. It demonstrates loading
|
||||
occluder from an EGG file and adding them to a CullTraverser.
|
||||
"""
|
||||
|
||||
# Load PRC data
|
||||
from panda3d.core import loadPrcFileData
|
||||
loadPrcFileData('', 'window-title Occluder Demo')
|
||||
loadPrcFileData('', 'sync-video false')
|
||||
loadPrcFileData('', 'show-frame-rate-meter true')
|
||||
loadPrcFileData('', 'texture-minfilter linear-mipmap-linear')
|
||||
#loadPrcFileData('', 'fake-view-frustum-cull true') # show culled nodes in red
|
||||
|
||||
# Import needed modules
|
||||
import random
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from panda3d.core import PerspectiveLens, TextNode, \
|
||||
TexGenAttrib, TextureStage, TransparencyAttrib, LPoint3, Texture
|
||||
|
||||
|
||||
def add_instructions(pos, msg):
|
||||
"""Function to put instructions on the screen."""
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.08, -pos - 0.04), scale=.05)
|
||||
|
||||
def add_title(text):
|
||||
"""Function to put title on the screen."""
|
||||
return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class Game(ShowBase):
|
||||
"""Sets up the game, camera, controls, and loads models."""
|
||||
def __init__(self):
|
||||
ShowBase.__init__(self)
|
||||
self.xray_mode = False
|
||||
self.show_model_bounds = False
|
||||
|
||||
# Display instructions
|
||||
add_title("Panda3D Tutorial: Occluder Culling")
|
||||
add_instructions(0.06, "[Esc]: Quit")
|
||||
add_instructions(0.12, "[W]: Move Forward")
|
||||
add_instructions(0.18, "[A]: Move Left")
|
||||
add_instructions(0.24, "[S]: Move Right")
|
||||
add_instructions(0.30, "[D]: Move Back")
|
||||
add_instructions(0.36, "Arrow Keys: Look Around")
|
||||
add_instructions(0.42, "[F]: Toggle Wireframe")
|
||||
add_instructions(0.48, "[X]: Toggle X-Ray Mode")
|
||||
add_instructions(0.54, "[B]: Toggle Bounding Volumes")
|
||||
|
||||
# Setup controls
|
||||
self.keys = {}
|
||||
for key in ['arrow_left', 'arrow_right', 'arrow_up', 'arrow_down',
|
||||
'a', 'd', 'w', 's']:
|
||||
self.keys[key] = 0
|
||||
self.accept(key, self.push_key, [key, 1])
|
||||
self.accept('shift-%s' % key, self.push_key, [key, 1])
|
||||
self.accept('%s-up' % key, self.push_key, [key, 0])
|
||||
self.accept('f', self.toggleWireframe)
|
||||
self.accept('x', self.toggle_xray_mode)
|
||||
self.accept('b', self.toggle_model_bounds)
|
||||
self.accept('escape', __import__('sys').exit, [0])
|
||||
self.disableMouse()
|
||||
|
||||
# Setup camera
|
||||
self.lens = PerspectiveLens()
|
||||
self.lens.setFov(60)
|
||||
self.lens.setNear(0.01)
|
||||
self.lens.setFar(1000.0)
|
||||
self.cam.node().setLens(self.lens)
|
||||
self.camera.setPos(-9, -0.5, 1)
|
||||
self.heading = -95.0
|
||||
self.pitch = 0.0
|
||||
|
||||
# Load level geometry
|
||||
self.level_model = self.loader.loadModel('models/level')
|
||||
self.level_model.reparentTo(self.render)
|
||||
self.level_model.setTexGen(TextureStage.getDefault(),
|
||||
TexGenAttrib.MWorldPosition)
|
||||
self.level_model.setTexProjector(TextureStage.getDefault(),
|
||||
self.render, self.level_model)
|
||||
self.level_model.setTexScale(TextureStage.getDefault(), 4)
|
||||
tex = self.loader.load3DTexture('models/tex_#.png')
|
||||
self.level_model.setTexture(tex)
|
||||
|
||||
# Load occluders
|
||||
occluder_model = self.loader.loadModel('models/occluders')
|
||||
occluder_nodepaths = occluder_model.findAllMatches('**/+OccluderNode')
|
||||
for occluder_nodepath in occluder_nodepaths:
|
||||
self.render.setOccluder(occluder_nodepath)
|
||||
occluder_nodepath.node().setDoubleSided(True)
|
||||
|
||||
# Randomly spawn some models to test the occluders
|
||||
self.models = []
|
||||
box_model = self.loader.loadModel('box')
|
||||
|
||||
for dummy in xrange(0, 500):
|
||||
pos = LPoint3((random.random() - 0.5) * 9,
|
||||
(random.random() - 0.5) * 9,
|
||||
random.random() * 8)
|
||||
box = box_model.copy_to(self.render)
|
||||
box.setScale(random.random() * 0.2 + 0.1)
|
||||
box.setPos(pos)
|
||||
box.setHpr(random.random() * 360,
|
||||
random.random() * 360,
|
||||
random.random() * 360)
|
||||
box.reparentTo(self.render)
|
||||
self.models.append(box)
|
||||
|
||||
self.taskMgr.add(self.update, 'main loop')
|
||||
|
||||
def push_key(self, key, value):
|
||||
"""Stores a value associated with a key."""
|
||||
self.keys[key] = value
|
||||
|
||||
def update(self, task):
|
||||
"""Updates the camera based on the keyboard input."""
|
||||
delta = globalClock.getDt()
|
||||
move_x = delta * 3 * -self.keys['a'] + delta * 3 * self.keys['d']
|
||||
move_z = delta * 3 * self.keys['s'] + delta * 3 * -self.keys['w']
|
||||
self.camera.setPos(self.camera, move_x, -move_z, 0)
|
||||
self.heading += (delta * 90 * self.keys['arrow_left'] +
|
||||
delta * 90 * -self.keys['arrow_right'])
|
||||
self.pitch += (delta * 90 * self.keys['arrow_up'] +
|
||||
delta * 90 * -self.keys['arrow_down'])
|
||||
self.camera.setHpr(self.heading, self.pitch, 0)
|
||||
return task.cont
|
||||
|
||||
def toggle_xray_mode(self):
|
||||
"""Toggle X-ray mode on and off. This is useful for seeing the
|
||||
effectiveness of the occluder culling."""
|
||||
self.xray_mode = not self.xray_mode
|
||||
if self.xray_mode:
|
||||
self.level_model.setColorScale((1, 1, 1, 0.5))
|
||||
self.level_model.setTransparency(TransparencyAttrib.MDual)
|
||||
else:
|
||||
self.level_model.setColorScaleOff()
|
||||
self.level_model.setTransparency(TransparencyAttrib.MNone)
|
||||
|
||||
def toggle_model_bounds(self):
|
||||
"""Toggle bounding volumes on and off on the models."""
|
||||
self.show_model_bounds = not self.show_model_bounds
|
||||
if self.show_model_bounds:
|
||||
for model in self.models:
|
||||
model.showBounds()
|
||||
else:
|
||||
for model in self.models:
|
||||
model.hideBounds()
|
||||
|
||||
game = Game()
|
||||
game.run()
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Author: Josh Enes
|
||||
Last Updated: 2015-03-13
|
||||
|
||||
This is a demo of Panda's portal-culling system. It demonstrates loading
|
||||
portals from an EGG file, and shows an example method of selecting the
|
||||
current cell using geoms and a collision ray.
|
||||
"""
|
||||
|
||||
# Some config options which can be changed.
|
||||
ENABLE_PORTALS = True # Set False to disable portal culling and see FPS drop!
|
||||
DEBUG_PORTALS = False # Set True to see visually which portals are used
|
||||
|
||||
# Load PRC data
|
||||
from panda3d.core import loadPrcFileData
|
||||
if ENABLE_PORTALS:
|
||||
loadPrcFileData('', 'allow-portal-cull true')
|
||||
if DEBUG_PORTALS:
|
||||
loadPrcFileData('', 'debug-portal-cull true')
|
||||
loadPrcFileData('', 'window-title Portal Demo')
|
||||
loadPrcFileData('', 'sync-video false')
|
||||
loadPrcFileData('', 'show-frame-rate-meter true')
|
||||
loadPrcFileData('', 'texture-minfilter linear-mipmap-linear')
|
||||
|
||||
# Import needed modules
|
||||
import random
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from panda3d.core import PerspectiveLens, NodePath, LVector3, LPoint3, \
|
||||
TexGenAttrib, TextureStage, TransparencyAttrib, CollisionTraverser, \
|
||||
CollisionHandlerQueue, TextNode, CollisionRay, CollisionNode
|
||||
|
||||
|
||||
def add_instructions(pos, msg):
|
||||
"""Function to put instructions on the screen."""
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.08, -pos - 0.04), scale=.05)
|
||||
|
||||
def add_title(text):
|
||||
"""Function to put title on the screen."""
|
||||
return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class Game(ShowBase):
|
||||
"""Sets up the game, camera, controls, and loads models."""
|
||||
def __init__(self):
|
||||
ShowBase.__init__(self)
|
||||
self.cellmanager = CellManager(self)
|
||||
self.xray_mode = False
|
||||
self.show_model_bounds = False
|
||||
|
||||
# Display instructions
|
||||
add_title("Panda3D Tutorial: Portal Culling")
|
||||
add_instructions(0.06, "[Esc]: Quit")
|
||||
add_instructions(0.12, "[W]: Move Forward")
|
||||
add_instructions(0.18, "[A]: Move Left")
|
||||
add_instructions(0.24, "[S]: Move Right")
|
||||
add_instructions(0.30, "[D]: Move Back")
|
||||
add_instructions(0.36, "Arrow Keys: Look Around")
|
||||
add_instructions(0.42, "[F]: Toggle Wireframe")
|
||||
add_instructions(0.48, "[X]: Toggle X-Ray Mode")
|
||||
add_instructions(0.54, "[B]: Toggle Bounding Volumes")
|
||||
|
||||
# Setup controls
|
||||
self.keys = {}
|
||||
for key in ['arrow_left', 'arrow_right', 'arrow_up', 'arrow_down',
|
||||
'a', 'd', 'w', 's']:
|
||||
self.keys[key] = 0
|
||||
self.accept(key, self.push_key, [key, 1])
|
||||
self.accept('shift-%s' % key, self.push_key, [key, 1])
|
||||
self.accept('%s-up' % key, self.push_key, [key, 0])
|
||||
self.accept('f', self.toggleWireframe)
|
||||
self.accept('x', self.toggle_xray_mode)
|
||||
self.accept('b', self.toggle_model_bounds)
|
||||
self.accept('escape', __import__('sys').exit, [0])
|
||||
self.disableMouse()
|
||||
|
||||
# Setup camera
|
||||
lens = PerspectiveLens()
|
||||
lens.setFov(60)
|
||||
lens.setNear(0.01)
|
||||
lens.setFar(1000.0)
|
||||
self.cam.node().setLens(lens)
|
||||
self.camera.setPos(-9, -0.5, 1)
|
||||
self.heading = -95.0
|
||||
self.pitch = 0.0
|
||||
|
||||
# Load level geometry
|
||||
self.level_model = self.loader.loadModel('models/level')
|
||||
self.level_model.reparentTo(self.render)
|
||||
self.level_model.setTexGen(TextureStage.getDefault(),
|
||||
TexGenAttrib.MWorldPosition)
|
||||
self.level_model.setTexProjector(TextureStage.getDefault(),
|
||||
self.render, self.level_model)
|
||||
self.level_model.setTexScale(TextureStage.getDefault(), 4)
|
||||
tex = self.loader.load3DTexture('models/tex_#.png')
|
||||
self.level_model.setTexture(tex)
|
||||
|
||||
# Load cells
|
||||
self.cellmanager.load_cells_from_model('models/cells')
|
||||
# Load portals
|
||||
self.cellmanager.load_portals_from_model('models/portals')
|
||||
|
||||
# Randomly spawn some models to test the portals
|
||||
self.models = []
|
||||
for dummy in xrange(0, 500):
|
||||
pos = LPoint3((random.random() - 0.5) * 6,
|
||||
(random.random() - 0.5) * 6,
|
||||
random.random() * 7)
|
||||
cell = self.cellmanager.get_cell(pos)
|
||||
if cell is None: # skip if the random position is not over a cell
|
||||
continue
|
||||
dist = self.cellmanager.get_dist_to_cell(pos)
|
||||
if dist > 1.5: # skip if the random position is too far from ground
|
||||
continue
|
||||
box = self.loader.loadModel('box')
|
||||
box.setScale(random.random() * 0.2 + 0.1)
|
||||
box.setPos(pos)
|
||||
box.setHpr(random.random() * 360,
|
||||
random.random() * 360,
|
||||
random.random() * 360)
|
||||
box.reparentTo(cell.nodepath)
|
||||
self.models.append(box)
|
||||
self.taskMgr.add(self.update, 'main loop')
|
||||
|
||||
def push_key(self, key, value):
|
||||
"""Stores a value associated with a key."""
|
||||
self.keys[key] = value
|
||||
|
||||
def update(self, task):
|
||||
"""Updates the camera based on the keyboard input. Once this is
|
||||
done, then the CellManager's update function is called."""
|
||||
delta = globalClock.getDt()
|
||||
move_x = delta * 3 * -self.keys['a'] + delta * 3 * self.keys['d']
|
||||
move_z = delta * 3 * self.keys['s'] + delta * 3 * -self.keys['w']
|
||||
self.camera.setPos(self.camera, move_x, -move_z, 0)
|
||||
self.heading += (delta * 90 * self.keys['arrow_left'] +
|
||||
delta * 90 * -self.keys['arrow_right'])
|
||||
self.pitch += (delta * 90 * self.keys['arrow_up'] +
|
||||
delta * 90 * -self.keys['arrow_down'])
|
||||
self.camera.setHpr(self.heading, self.pitch, 0)
|
||||
if ENABLE_PORTALS:
|
||||
self.cellmanager.update()
|
||||
return task.cont
|
||||
|
||||
def toggle_xray_mode(self):
|
||||
"""Toggle X-ray mode on and off. This is useful for seeing the
|
||||
effectiveness of the portal culling."""
|
||||
self.xray_mode = not self.xray_mode
|
||||
if self.xray_mode:
|
||||
self.level_model.setColorScale((1, 1, 1, 0.5))
|
||||
self.level_model.setTransparency(TransparencyAttrib.MDual)
|
||||
else:
|
||||
self.level_model.setColorScaleOff()
|
||||
self.level_model.setTransparency(TransparencyAttrib.MNone)
|
||||
|
||||
def toggle_model_bounds(self):
|
||||
"""Toggle bounding volumes on and off on the models."""
|
||||
self.show_model_bounds = not self.show_model_bounds
|
||||
if self.show_model_bounds:
|
||||
for model in self.models:
|
||||
model.showBounds()
|
||||
else:
|
||||
for model in self.models:
|
||||
model.hideBounds()
|
||||
|
||||
|
||||
class CellManager(object):
|
||||
"""Creates a collision ray and collision traverser to use for
|
||||
selecting the current cell."""
|
||||
def __init__(self, game):
|
||||
self.game = game
|
||||
self.cells = {}
|
||||
self.cells_by_collider = {}
|
||||
self.cell_picker_world = NodePath('cell_picker_world')
|
||||
self.ray = CollisionRay()
|
||||
self.ray.setDirection(LVector3.down())
|
||||
cnode = CollisionNode('cell_raycast_cnode')
|
||||
self.ray_nodepath = self.cell_picker_world.attachNewNode(cnode)
|
||||
self.ray_nodepath.node().addSolid(self.ray)
|
||||
self.ray_nodepath.node().setIntoCollideMask(0) # not for colliding into
|
||||
self.ray_nodepath.node().setFromCollideMask(1)
|
||||
self.traverser = CollisionTraverser('traverser')
|
||||
self.last_known_cell = None
|
||||
|
||||
def add_cell(self, collider, name):
|
||||
"""Add a new cell."""
|
||||
cell = Cell(self, name, collider)
|
||||
self.cells[name] = cell
|
||||
self.cells_by_collider[collider.node()] = cell
|
||||
|
||||
def get_cell(self, pos):
|
||||
"""Given a position, return the nearest cell below that position.
|
||||
If no cell is found, returns None."""
|
||||
self.ray.setOrigin(pos)
|
||||
queue = CollisionHandlerQueue()
|
||||
self.traverser.addCollider(self.ray_nodepath, queue)
|
||||
self.traverser.traverse(self.cell_picker_world)
|
||||
self.traverser.removeCollider(self.ray_nodepath)
|
||||
queue.sortEntries()
|
||||
if not queue.getNumEntries():
|
||||
return None
|
||||
entry = queue.getEntry(0)
|
||||
cnode = entry.getIntoNode()
|
||||
try:
|
||||
return self.cells_by_collider[cnode]
|
||||
except KeyError:
|
||||
raise Warning('collision ray collided with something '
|
||||
'other than a cell: %s' % cnode)
|
||||
|
||||
def get_dist_to_cell(self, pos):
|
||||
"""Given a position, return the distance to the nearest cell
|
||||
below that position. If no cell is found, returns None."""
|
||||
self.ray.setOrigin(pos)
|
||||
queue = CollisionHandlerQueue()
|
||||
self.traverser.addCollider(self.ray_nodepath, queue)
|
||||
self.traverser.traverse(self.cell_picker_world)
|
||||
self.traverser.removeCollider(self.ray_nodepath)
|
||||
queue.sortEntries()
|
||||
if not queue.getNumEntries():
|
||||
return None
|
||||
entry = queue.getEntry(0)
|
||||
return (entry.getSurfacePoint(self.cell_picker_world) - pos).length()
|
||||
|
||||
def load_cells_from_model(self, modelpath):
|
||||
"""Loads cells from an EGG file. Cells must be named in the
|
||||
format "cell#" to be loaded by this function."""
|
||||
cell_model = self.game.loader.loadModel(modelpath)
|
||||
for collider in cell_model.findAllMatches('**/+GeomNode'):
|
||||
name = collider.getName()
|
||||
if name.startswith('cell'):
|
||||
self.add_cell(collider, name[4:])
|
||||
cell_model.removeNode()
|
||||
|
||||
def load_portals_from_model(self, modelpath):
|
||||
"""Loads portals from an EGG file. Portals must be named in the
|
||||
format "portal_#to#_*" to be loaded by this function, whereby the
|
||||
first # is the from cell, the second # is the into cell, and * can
|
||||
be anything."""
|
||||
portal_model = loader.loadModel(modelpath)
|
||||
portal_nodepaths = portal_model.findAllMatches('**/+PortalNode')
|
||||
for portal_nodepath in portal_nodepaths:
|
||||
name = portal_nodepath.getName()
|
||||
if name.startswith('portal_'):
|
||||
from_cell_id, into_cell_id = name.split('_')[1].split('to')
|
||||
try:
|
||||
from_cell = self.cells[from_cell_id]
|
||||
except KeyError:
|
||||
print ('could not load portal "%s" because cell "%s"'
|
||||
'does not exist' % (name, from_cell_id))
|
||||
continue
|
||||
try:
|
||||
into_cell = self.cells[into_cell_id]
|
||||
except KeyError:
|
||||
print ('could not load portal "%s" because cell "%s"'
|
||||
'does not exist' % (name, into_cell_id))
|
||||
continue
|
||||
from_cell.add_portal(portal_nodepath, into_cell)
|
||||
portal_model.removeNode()
|
||||
|
||||
def update(self):
|
||||
"""Show the cell the camera is currently in and hides the rest.
|
||||
If the camera is not in a cell, use the last known cell that the
|
||||
camera was in. If the camera has not yet been in a cell, then all
|
||||
cells will be hidden."""
|
||||
camera_pos = self.game.camera.getPos(self.game.render)
|
||||
for cell in self.cells:
|
||||
self.cells[cell].nodepath.hide()
|
||||
current_cell = self.get_cell(camera_pos)
|
||||
if current_cell is None:
|
||||
if self.last_known_cell is None:
|
||||
return
|
||||
self.last_known_cell.nodepath.show()
|
||||
else:
|
||||
self.last_known_cell = current_cell
|
||||
current_cell.nodepath.show()
|
||||
|
||||
|
||||
class Cell(object):
|
||||
"""The Cell class is a handy way to keep an association between
|
||||
all the related nodes and information of a cell."""
|
||||
def __init__(self, cellmanager, name, collider):
|
||||
self.cellmanager = cellmanager
|
||||
self.name = name
|
||||
self.collider = collider
|
||||
self.collider.reparentTo(self.cellmanager.cell_picker_world)
|
||||
self.collider.setCollideMask(1)
|
||||
self.collider.hide()
|
||||
self.nodepath = NodePath('cell_%s_root' % name)
|
||||
self.nodepath.reparentTo(self.cellmanager.game.render)
|
||||
self.portals = []
|
||||
|
||||
def add_portal(self, portal, cell_out):
|
||||
"""Add a portal from this cell going into another one."""
|
||||
portal.reparentTo(self.nodepath)
|
||||
portal.node().setCellIn(self.nodepath)
|
||||
portal.node().setCellOut(cell_out.nodepath)
|
||||
self.portals.append(portal)
|
||||
|
||||
game = Game()
|
||||
game.run()
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Jason Pratt (pratt@andrew.cmu.edu)
|
||||
# Last Updated: 2015-03-13
|
||||
#
|
||||
# This project demonstrates how to use various types of
|
||||
# lighting
|
||||
#
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import PerspectiveLens
|
||||
from panda3d.core import NodePath
|
||||
from panda3d.core import AmbientLight, DirectionalLight
|
||||
from panda3d.core import PointLight, Spotlight
|
||||
from panda3d.core import TextNode
|
||||
from panda3d.core import Material
|
||||
from panda3d.core import LVector3
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
import math
|
||||
import sys
|
||||
import colorsys
|
||||
|
||||
# Simple function to keep a value in a given range (by default 0 to 1)
|
||||
def clamp(i, mn=0, mx=1):
|
||||
return min(max(i, mn), mx)
|
||||
|
||||
|
||||
class DiscoLightsDemo(ShowBase):
|
||||
|
||||
# Macro-like function to reduce the amount of code needed to create the
|
||||
# onscreen instructions
|
||||
def makeStatusLabel(self, i):
|
||||
return OnscreenText(
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
style=1, fg=(1, 1, 0, 1), shadow=(0, 0, 0, .4),
|
||||
pos=(0.06, -0.1 -(.06 * i)), scale=.05, mayChange=True)
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# The main initialization of our class
|
||||
# This creates the on screen title that is in every tutorial
|
||||
self.title = OnscreenText(text="Panda3D: Tutorial - Lighting",
|
||||
style=1, fg=(1, 1, 0, 1), shadow=(0, 0, 0, 0.5),
|
||||
pos=(0.87, -0.95), scale = .07)
|
||||
|
||||
# Creates labels used for onscreen instructions
|
||||
self.ambientText = self.makeStatusLabel(0)
|
||||
self.directionalText = self.makeStatusLabel(1)
|
||||
self.spotlightText = self.makeStatusLabel(2)
|
||||
self.pointLightText = self.makeStatusLabel(3)
|
||||
self.spinningText = self.makeStatusLabel(4)
|
||||
self.ambientBrightnessText = self.makeStatusLabel(5)
|
||||
self.directionalBrightnessText = self.makeStatusLabel(6)
|
||||
self.spotlightBrightnessText = self.makeStatusLabel(7)
|
||||
self.spotlightExponentText = self.makeStatusLabel(8)
|
||||
self.lightingPerPixelText = self.makeStatusLabel(9)
|
||||
self.lightingShadowsText = self.makeStatusLabel(10)
|
||||
|
||||
self.disco = loader.loadModel("models/disco_hall")
|
||||
self.disco.reparentTo(render)
|
||||
self.disco.setPosHpr(0, 50, -4, 90, 0, 0)
|
||||
|
||||
# First we create an ambient light. All objects are affected by ambient
|
||||
# light equally
|
||||
# Create and name the ambient light
|
||||
self.ambientLight = render.attachNewNode(AmbientLight("ambientLight"))
|
||||
# Set the color of the ambient light
|
||||
self.ambientLight.node().setColor((.1, .1, .1, 1))
|
||||
# add the newly created light to the lightAttrib
|
||||
|
||||
# Now we create a directional light. Directional lights add shading from a
|
||||
# given angle. This is good for far away sources like the sun
|
||||
self.directionalLight = render.attachNewNode(
|
||||
DirectionalLight("directionalLight"))
|
||||
self.directionalLight.node().setColor((.35, .35, .35, 1))
|
||||
# The direction of a directional light is set as a 3D vector
|
||||
self.directionalLight.node().setDirection(LVector3(1, 1, -2))
|
||||
# These settings are necessary for shadows to work correctly
|
||||
self.directionalLight.setZ(6)
|
||||
dlens = self.directionalLight.node().getLens()
|
||||
dlens.setFilmSize(41, 21)
|
||||
dlens.setNearFar(50, 75)
|
||||
# self.directionalLight.node().showFrustum()
|
||||
|
||||
# Now we create a spotlight. Spotlights light objects in a given cone
|
||||
# They are good for simulating things like flashlights
|
||||
self.spotlight = camera.attachNewNode(Spotlight("spotlight"))
|
||||
self.spotlight.node().setColor((.45, .45, .45, 1))
|
||||
self.spotlight.node().setSpecularColor((0, 0, 0, 1))
|
||||
# The cone of a spotlight is controlled by it's lens. This creates the
|
||||
# lens
|
||||
self.spotlight.node().setLens(PerspectiveLens())
|
||||
# This sets the Field of View (fov) of the lens, in degrees for width
|
||||
# and height. The lower the numbers, the tighter the spotlight.
|
||||
self.spotlight.node().getLens().setFov(16, 16)
|
||||
# Attenuation controls how the light fades with distance. The three
|
||||
# values represent the three attenuation constants (constant, linear,
|
||||
# and quadratic) in the internal lighting equation. The higher the
|
||||
# numbers the shorter the light goes.
|
||||
self.spotlight.node().setAttenuation(LVector3(1, 0.0, 0.0))
|
||||
# This exponent value sets how soft the edge of the spotlight is.
|
||||
# 0 means a hard edge. 128 means a very soft edge.
|
||||
self.spotlight.node().setExponent(60.0)
|
||||
|
||||
# Now we create three colored Point lights. Point lights are lights that
|
||||
# radiate from a single point, like a light bulb. Like spotlights, they
|
||||
# are given position by attaching them to NodePaths in the world
|
||||
self.redHelper = loader.loadModel('models/sphere')
|
||||
self.redHelper.setColor((1, 0, 0, 1))
|
||||
self.redHelper.setPos(-6.5, -3.75, 0)
|
||||
self.redHelper.setScale(.25)
|
||||
self.redPointLight = self.redHelper.attachNewNode(
|
||||
PointLight("redPointLight"))
|
||||
self.redPointLight.node().setColor((.35, 0, 0, 1))
|
||||
self.redPointLight.node().setAttenuation(LVector3(.1, 0.04, 0.0))
|
||||
|
||||
# The green point light and helper
|
||||
self.greenHelper = loader.loadModel('models/sphere')
|
||||
self.greenHelper.setColor((0, 1, 0, 1))
|
||||
self.greenHelper.setPos(0, 7.5, 0)
|
||||
self.greenHelper.setScale(.25)
|
||||
self.greenPointLight = self.greenHelper.attachNewNode(
|
||||
PointLight("greenPointLight"))
|
||||
self.greenPointLight.node().setAttenuation(LVector3(.1, .04, .0))
|
||||
self.greenPointLight.node().setColor((0, .35, 0, 1))
|
||||
|
||||
# The blue point light and helper
|
||||
self.blueHelper = loader.loadModel('models/sphere')
|
||||
self.blueHelper.setColor((0, 0, 1, 1))
|
||||
self.blueHelper.setPos(6.5, -3.75, 0)
|
||||
self.blueHelper.setScale(.25)
|
||||
self.bluePointLight = self.blueHelper.attachNewNode(
|
||||
PointLight("bluePointLight"))
|
||||
self.bluePointLight.node().setAttenuation(LVector3(.1, 0.04, 0.0))
|
||||
self.bluePointLight.node().setColor((0, 0, .35, 1))
|
||||
self.bluePointLight.node().setSpecularColor((1, 1, 1, 1))
|
||||
|
||||
# Create a dummy node so the lights can be spun with one command
|
||||
self.pointLightHelper = render.attachNewNode("pointLightHelper")
|
||||
self.pointLightHelper.setPos(0, 50, 11)
|
||||
self.redHelper.reparentTo(self.pointLightHelper)
|
||||
self.greenHelper.reparentTo(self.pointLightHelper)
|
||||
self.blueHelper.reparentTo(self.pointLightHelper)
|
||||
|
||||
# Finally we store the lights on the root of the scene graph.
|
||||
# This will cause them to affect everything in the scene.
|
||||
render.setLight(self.ambientLight)
|
||||
render.setLight(self.directionalLight)
|
||||
render.setLight(self.spotlight)
|
||||
render.setLight(self.redPointLight)
|
||||
render.setLight(self.greenPointLight)
|
||||
render.setLight(self.bluePointLight)
|
||||
|
||||
# Create and start interval to spin the lights, and a variable to
|
||||
# manage them.
|
||||
self.pointLightsSpin = self.pointLightHelper.hprInterval(
|
||||
6, LVector3(360, 0, 0))
|
||||
self.pointLightsSpin.loop()
|
||||
self.arePointLightsSpinning = True
|
||||
|
||||
# Per-pixel lighting and shadows are initially off
|
||||
self.perPixelEnabled = False
|
||||
self.shadowsEnabled = False
|
||||
|
||||
# listen to keys for controlling the lights
|
||||
self.accept("escape", sys.exit)
|
||||
self.accept("a", self.toggleLights, [[self.ambientLight]])
|
||||
self.accept("d", self.toggleLights, [[self.directionalLight]])
|
||||
self.accept("s", self.toggleLights, [[self.spotlight]])
|
||||
self.accept("p", self.toggleLights, [[self.redPointLight,
|
||||
self.greenPointLight,
|
||||
self.bluePointLight]])
|
||||
self.accept("r", self.toggleSpinningPointLights)
|
||||
self.accept("l", self.togglePerPixelLighting)
|
||||
self.accept("e", self.toggleShadows)
|
||||
self.accept("z", self.addBrightness, [self.ambientLight, -.05])
|
||||
self.accept("x", self.addBrightness, [self.ambientLight, .05])
|
||||
self.accept("c", self.addBrightness, [self.directionalLight, -.05])
|
||||
self.accept("v", self.addBrightness, [self.directionalLight, .05])
|
||||
self.accept("b", self.addBrightness, [self.spotlight, -.05])
|
||||
self.accept("n", self.addBrightness, [self.spotlight, .05])
|
||||
self.accept("q", self.adjustSpotlightExponent, [self.spotlight, -1])
|
||||
self.accept("w", self.adjustSpotlightExponent, [self.spotlight, 1])
|
||||
|
||||
# Finally call the function that builds the instruction texts
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function takes a list of lights and toggles their state. It takes in a
|
||||
# list so that more than one light can be toggled in a single command
|
||||
def toggleLights(self, lights):
|
||||
for light in lights:
|
||||
# If the given light is in our lightAttrib, remove it.
|
||||
# This has the effect of turning off the light
|
||||
if render.hasLight(light):
|
||||
render.clearLight(light)
|
||||
# Otherwise, add it back. This has the effect of turning the light
|
||||
# on
|
||||
else:
|
||||
render.setLight(light)
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function toggles the spinning of the point intervals by pausing and
|
||||
# resuming the interval
|
||||
def toggleSpinningPointLights(self):
|
||||
if self.arePointLightsSpinning:
|
||||
self.pointLightsSpin.pause()
|
||||
else:
|
||||
self.pointLightsSpin.resume()
|
||||
self.arePointLightsSpinning = not self.arePointLightsSpinning
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function turns per-pixel lighting on or off.
|
||||
def togglePerPixelLighting(self):
|
||||
if self.perPixelEnabled:
|
||||
self.perPixelEnabled = False
|
||||
render.clearShader()
|
||||
else:
|
||||
self.perPixelEnabled = True
|
||||
render.setShaderAuto()
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function turns shadows on or off.
|
||||
def toggleShadows(self):
|
||||
if self.shadowsEnabled:
|
||||
self.shadowsEnabled = False
|
||||
self.directionalLight.node().setShadowCaster(False)
|
||||
else:
|
||||
if not self.perPixelEnabled:
|
||||
self.togglePerPixelLighting()
|
||||
self.shadowsEnabled = True
|
||||
self.directionalLight.node().setShadowCaster(True, 512, 512)
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function changes the spotlight's exponent. It is kept to the range
|
||||
# 0 to 128. Going outside of this range causes an error
|
||||
def adjustSpotlightExponent(self, spotlight, amount):
|
||||
e = clamp(spotlight.node().getExponent() + amount, 0, 128)
|
||||
spotlight.node().setExponent(e)
|
||||
self.updateStatusLabel()
|
||||
|
||||
# This function reads the color of the light, uses a built-in python function
|
||||
#(from the library colorsys) to convert from RGB (red, green, blue) color
|
||||
# representation to HSB (hue, saturation, brightness), so that we can get the
|
||||
# brighteness of a light, change it, and then convert it back to rgb to chagne
|
||||
# the light's color
|
||||
def addBrightness(self, light, amount):
|
||||
color = light.node().getColor()
|
||||
h, s, b = colorsys.rgb_to_hsv(color[0], color[1], color[2])
|
||||
brightness = clamp(b + amount)
|
||||
r, g, b = colorsys.hsv_to_rgb(h, s, brightness)
|
||||
light.node().setColor((r, g, b, 1))
|
||||
|
||||
self.updateStatusLabel()
|
||||
|
||||
# Builds the onscreen instruction labels
|
||||
def updateStatusLabel(self):
|
||||
self.updateLabel(self.ambientText, "(a) ambient is",
|
||||
render.hasLight(self.ambientLight))
|
||||
self.updateLabel(self.directionalText, "(d) directional is",
|
||||
render.hasLight(self.directionalLight))
|
||||
self.updateLabel(self.spotlightText, "(s) spotlight is",
|
||||
render.hasLight(self.spotlight))
|
||||
self.updateLabel(self.pointLightText, "(p) point lights are",
|
||||
render.hasLight(self.redPointLight))
|
||||
self.updateLabel(self.spinningText, "(r) point light spinning is",
|
||||
self.arePointLightsSpinning)
|
||||
self.ambientBrightnessText.setText(
|
||||
"(z,x) Ambient Brightness: " +
|
||||
self.getBrightnessString(self.ambientLight))
|
||||
self.directionalBrightnessText.setText(
|
||||
"(c,v) Directional Brightness: " +
|
||||
self.getBrightnessString(self.directionalLight))
|
||||
self.spotlightBrightnessText.setText(
|
||||
"(b,n) Spotlight Brightness: " +
|
||||
self.getBrightnessString(self.spotlight))
|
||||
self.spotlightExponentText.setText(
|
||||
"(q,w) Spotlight Exponent: " +
|
||||
str(int(self.spotlight.node().getExponent())))
|
||||
self.updateLabel(self.lightingPerPixelText, "(l) Per-pixel lighting is",
|
||||
self.perPixelEnabled)
|
||||
self.updateLabel(self.lightingShadowsText, "(e) Shadows are",
|
||||
self.shadowsEnabled)
|
||||
|
||||
# Appends eitehr (on) or (off) to the base string based on the bassed value
|
||||
def updateLabel(self, obj, base, var):
|
||||
if var:
|
||||
s = " (on)"
|
||||
else:
|
||||
s = " (off)"
|
||||
obj.setText(base + s)
|
||||
|
||||
# Returns the brightness of a light as a string to put it in the instruction
|
||||
# labels
|
||||
def getBrightnessString(self, light):
|
||||
color = light.node().getColor()
|
||||
h, s, b = colorsys.rgb_to_hsv(color[0], color[1], color[2])
|
||||
return "%.2f" % b
|
||||
|
||||
|
||||
# Make an instance of our class and run the demo
|
||||
demo = DiscoLightsDemo()
|
||||
demo.run()
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//Cg
|
||||
//
|
||||
// time
|
||||
//
|
||||
// You need to pass the frame time here.
|
||||
//
|
||||
// desat.x
|
||||
//
|
||||
// Desaturation level. If zero, the bloom's color is equal to
|
||||
// the color of the input pixel. If one, the bloom's color is
|
||||
// white.
|
||||
//
|
||||
// trigger.x
|
||||
//
|
||||
// Must be equal to mintrigger.
|
||||
//
|
||||
// mintrigger is the minimum brightness to trigger a bloom,
|
||||
// and maxtrigger is the brightness at which the bloom
|
||||
// reaches maximum intensity.
|
||||
//
|
||||
// trigger.y
|
||||
//
|
||||
// Must be equal to (1.0/(maxtrigger-mintrigger)) where
|
||||
//
|
||||
// mintrigger is the minimum brightness to trigger a bloom,
|
||||
// and maxtrigger is the brightness at which the bloom
|
||||
// reaches maximum intensity.
|
||||
//
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 trans_model_to_clip,
|
||||
out float4 l_position : POSITION,
|
||||
out float4 l_texcoord0 : TEXCOORD0)
|
||||
{
|
||||
l_position = mul(mat_modelproj, vtx_position);
|
||||
l_texcoord0 = mul(trans_model_to_clip, vtx_position);
|
||||
l_texcoord0.z = l_texcoord0.w;
|
||||
}
|
||||
|
||||
void fshader(float4 l_texcoord0 : TEXCOORD0,
|
||||
uniform sampler2D k_screen : TEXUNIT1,
|
||||
uniform sampler2D k_waves : TEXUNIT0,
|
||||
uniform float4 texpad_screen,
|
||||
in uniform float sys_time,
|
||||
out float4 o_color : COLOR)
|
||||
{
|
||||
float3 screen = l_texcoord0.xyz / l_texcoord0.w;
|
||||
float2 texcoords = float2(screen.xy) * texpad_screen.xy + texpad_screen.xy;
|
||||
float4 disturbance = tex2D(k_waves, texcoords);
|
||||
//o_color = tex2D(k_screen, texcoords + disturbance.xy * 0.05 * disturbance.z * sys_time.x * 1);
|
||||
o_color = tex2D(k_screen, texcoords + disturbance.xy * 0.05 * disturbance.z * sin(sys_time.x) * 1);
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Tree Form starplant@gmail.com
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import FrameBufferProperties, TextNode, BitMask32, LPoint3
|
||||
from panda3d.core import WindowProperties, GraphicsOutput, Texture, GraphicsPipe
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from sys import exit
|
||||
|
||||
# Function to put instructions on the screen.
|
||||
def addInstructions(pos, msg):
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
|
||||
pos=(-1.25, pos), align=TextNode.ALeft, scale=.05)
|
||||
|
||||
# Function to put title on the screen.
|
||||
def addTitle(text):
|
||||
return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
|
||||
pos=(1.25, -0.95), align=TextNode.ARight, scale=.07)
|
||||
|
||||
|
||||
class DistortionDemo(ShowBase):
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
|
||||
if not base.win.getGsg().getSupportsBasicShaders():
|
||||
t = addTitle("Distortion Demo: Video driver says Cg shaders not supported.")
|
||||
return
|
||||
|
||||
self.disableMouse()
|
||||
self.setBackgroundColor(0, 0, 0)
|
||||
|
||||
# Show the instructions
|
||||
self.title = addTitle("Panda3D: Tutorial - Distortion Effect")
|
||||
self.inst1 = addInstructions(0.92, "ESC: Quit")
|
||||
self.inst2 = addInstructions(0.86, "Space: Toggle distortion filter On/Off")
|
||||
self.inst4 = addInstructions(0.80, "V: View the render-to-texture results")
|
||||
|
||||
# Load background
|
||||
self.seascape = loader.loadModel("models/plane")
|
||||
self.seascape.reparentTo(render)
|
||||
self.seascape.setPosHpr(0, 145, 0, 0, 0, 0)
|
||||
self.seascape.setScale(100)
|
||||
self.seascape.setTexture(loader.loadTexture("models/ocean.jpg"))
|
||||
|
||||
# Create the distortion buffer. This buffer renders like a normal
|
||||
# scene,
|
||||
self.distortionBuffer = self.makeFBO("model buffer")
|
||||
self.distortionBuffer.setSort(-3)
|
||||
self.distortionBuffer.setClearColor((0, 0, 0, 0))
|
||||
|
||||
# We have to attach a camera to the distortion buffer. The distortion camera
|
||||
# must have the same frustum as the main camera. As long as the aspect
|
||||
# ratios match, the rest will take care of itself.
|
||||
distortionCamera = self.makeCamera(self.distortionBuffer, scene=render,
|
||||
lens=self.cam.node().getLens(), mask=BitMask32.bit(4))
|
||||
|
||||
# load the object with the distortion
|
||||
self.distortionObject = loader.loadModel("models/boat")
|
||||
self.distortionObject.setScale(1)
|
||||
self.distortionObject.setPos(0, 20, -3)
|
||||
self.distortionObject.hprInterval(10, LPoint3(360, 0, 0)).loop()
|
||||
self.distortionObject.reparentTo(render)
|
||||
|
||||
# Create the shader that will determime what parts of the scene will
|
||||
# distortion
|
||||
distortionShader = loader.loadShader("distortion.sha")
|
||||
self.distortionObject.setShader(distortionShader)
|
||||
self.distortionObject.hide(BitMask32.bit(4))
|
||||
|
||||
# Textures
|
||||
tex1 = loader.loadTexture("models/water.png")
|
||||
self.distortionObject.setShaderInput("waves", tex1)
|
||||
|
||||
self.texDistortion = Texture()
|
||||
self.distortionBuffer.addRenderTexture(
|
||||
self.texDistortion, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
|
||||
self.distortionObject.setShaderInput("screen", self.texDistortion)
|
||||
|
||||
# Panda contains a built-in viewer that lets you view the results of
|
||||
# your render-to-texture operations. This code configures the viewer.
|
||||
self.accept("v", self.bufferViewer.toggleEnable)
|
||||
self.accept("V", self.bufferViewer.toggleEnable)
|
||||
self.bufferViewer.setPosition("llcorner")
|
||||
self.bufferViewer.setLayout("hline")
|
||||
self.bufferViewer.setCardSize(0.652, 0)
|
||||
|
||||
# event handling
|
||||
self.accept("space", self.toggleDistortion)
|
||||
self.accept("escape", exit, [0])
|
||||
self.distortionOn = True
|
||||
|
||||
def makeFBO(self, name):
|
||||
# This routine creates an offscreen buffer. All the complicated
|
||||
# parameters are basically demanding capabilities from the offscreen
|
||||
# buffer - we demand that it be able to render to texture on every
|
||||
# bitplane, that it can support aux bitplanes, that it track
|
||||
# the size of the host window, that it can render to texture
|
||||
# cumulatively, and so forth.
|
||||
winprops = WindowProperties()
|
||||
props = FrameBufferProperties()
|
||||
props.setRgbColor(1)
|
||||
return self.graphicsEngine.makeOutput(
|
||||
self.pipe, "model buffer", -2, props, winprops,
|
||||
GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFRefuseWindow,
|
||||
self.win.getGsg(), self.win)
|
||||
|
||||
def toggleDistortion(self):
|
||||
# Toggles the distortion on/off.
|
||||
if self.distortionOn:
|
||||
self.distortionObject.hide()
|
||||
else:
|
||||
self.distortionObject.show()
|
||||
self.distortionOn = not(self.distortionOn)
|
||||
|
||||
demo = DistortionDemo()
|
||||
demo.run()
|
||||
|
After Width: | Height: | Size: 168 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
xÚ<EFBFBD>’Ákƒ0Æïƒþ<0F>Þc»[¶zÒÒÂN#£©˜¼³Q)þï}±Ñ‰l‡…h4ß÷|¿|D¾A´§O-œÜ·<C39C>“ª€+¼&Ýbñ@ß RR;Òi°T¢¹¬*0µÐ2Uïá…¤%UøÏøÖâ—) ¢‡>•Þ~sVèæŒVõ}¦U¦/`¡??Jëä¥D¬kÿ!Œdé7M¨ë
|
||||
`£<04>déã}ÊF<C38A>¿TÔ>WF#a}²àŽƒ>wÛç'/³0«›£ó9:‚̢ȫ 9.3‹Ì¼ž‘#Àÿˆ|Ÿy‰u[¡.~NÿONØèNž½¹¢ýæÔ•‡õ/?T7AùûxÒ³I
|
||||
|
After Width: | Height: | Size: 87 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
//Cg
|
||||
//
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
out float4 l_position : POSITION,
|
||||
out float4 l_pos : TEXCOORD0,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 trans_model_to_clip)
|
||||
{
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_pos=mul(trans_model_to_clip, vtx_position);
|
||||
l_pos.z = l_pos.w;
|
||||
}
|
||||
|
||||
void fshader(float4 l_pos: TEXCOORD0,
|
||||
float4 l_scale: TEXCOORD1,
|
||||
uniform sampler2D k_texnormal : TEXUNIT0,
|
||||
uniform sampler2D k_texalbedo : TEXUNIT1,
|
||||
uniform sampler2D k_texdepth : TEXUNIT2,
|
||||
uniform float4 texpad_texnormal,
|
||||
uniform float4 k_proj,
|
||||
uniform float4 vspos_model,
|
||||
uniform float4 k_lightcolor,
|
||||
uniform float4 row0_model_to_view,
|
||||
out float4 o_color: COLOR)
|
||||
{
|
||||
float3 screen = l_pos.xyz / l_pos.w;
|
||||
float2 texcoords = float2(screen.xy) * texpad_texnormal.xy + texpad_texnormal.xy;
|
||||
|
||||
float4 albedo = tex2D(k_texalbedo, texcoords);
|
||||
float4 normal = tex2D(k_texnormal, texcoords);
|
||||
float depth = tex2D(k_texdepth, texcoords);
|
||||
|
||||
float3 view = (screen.xzy * k_proj.xyz) / (depth + k_proj.w);
|
||||
|
||||
float3 lightvec = float3(vspos_model) - view;
|
||||
float lightdist = length(lightvec);
|
||||
float3 lightdir = lightvec / lightdist;
|
||||
float scaledist = (lightdist / row0_model_to_view.x);
|
||||
float falloff = saturate(1.0 - scaledist);
|
||||
float brite = falloff * falloff * dot(lightdir, float3(normal));
|
||||
o_color = albedo * k_lightcolor * brite;
|
||||
o_color.a = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,416 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Author: Josh Yelon
|
||||
# Date: 7/11/2005
|
||||
#
|
||||
# See the associated manual page for an explanation.
|
||||
#
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import FrameBufferProperties, WindowProperties
|
||||
from panda3d.core import GraphicsPipe, GraphicsOutput
|
||||
from panda3d.core import Filename, Texture, Shader
|
||||
from panda3d.core import RenderState, CardMaker
|
||||
from panda3d.core import PandaNode, TextNode, NodePath
|
||||
from panda3d.core import RenderAttrib, AlphaTestAttrib, ColorBlendAttrib
|
||||
from panda3d.core import CullFaceAttrib, DepthTestAttrib, DepthWriteAttrib
|
||||
from panda3d.core import LPoint3, LVector3, BitMask32
|
||||
from direct.gui.OnscreenText import OnscreenText
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from direct.interval.MetaInterval import Sequence
|
||||
from direct.task.Task import Task
|
||||
from direct.actor.Actor import Actor
|
||||
import sys
|
||||
import os
|
||||
import random
|
||||
|
||||
# Function to put instructions on the screen.
|
||||
def addInstructions(pos, msg):
|
||||
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
|
||||
parent=base.a2dTopLeft, align=TextNode.ALeft,
|
||||
pos=(0.08, -pos - 0.04), scale=.05)
|
||||
|
||||
# Function to put title on the screen.
|
||||
def addTitle(text):
|
||||
return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
|
||||
parent=base.a2dBottomRight, align=TextNode.ARight,
|
||||
fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
|
||||
|
||||
|
||||
class FireflyDemo(ShowBase):
|
||||
def __init__(self):
|
||||
# Initialize the ShowBase class from which we inherit, which will
|
||||
# create a window and set up everything we need for rendering into it.
|
||||
ShowBase.__init__(self)
|
||||
self.setBackgroundColor((0, 0, 0, 0))
|
||||
|
||||
# Preliminary capabilities check.
|
||||
|
||||
if not self.win.getGsg().getSupportsBasicShaders():
|
||||
self.t = addTitle("Firefly Demo: Video driver reports that Cg "
|
||||
"shaders are not supported.")
|
||||
return
|
||||
if not self.win.getGsg().getSupportsDepthTexture():
|
||||
self.t = addTitle("Firefly Demo: Video driver reports that depth "
|
||||
"textures are not supported.")
|
||||
return
|
||||
|
||||
# This algorithm uses two offscreen buffers, one of which has
|
||||
# an auxiliary bitplane, and the offscreen buffers share a single
|
||||
# depth buffer. This is a heck of a complicated buffer setup.
|
||||
|
||||
self.modelbuffer = self.makeFBO("model buffer", 1)
|
||||
self.lightbuffer = self.makeFBO("light buffer", 0)
|
||||
|
||||
# Creation of a high-powered buffer can fail, if the graphics card
|
||||
# doesn't support the necessary OpenGL extensions.
|
||||
|
||||
if self.modelbuffer is None or self.lightbuffer is None:
|
||||
self.t = addTitle("Toon Shader: Video driver does not support "
|
||||
"multiple render targets")
|
||||
return
|
||||
|
||||
# Create four render textures: depth, normal, albedo, and final.
|
||||
# attach them to the various bitplanes of the offscreen buffers.
|
||||
|
||||
self.texDepth = Texture()
|
||||
self.texDepth.setFormat(Texture.FDepthStencil)
|
||||
self.texAlbedo = Texture()
|
||||
self.texNormal = Texture()
|
||||
self.texFinal = Texture()
|
||||
|
||||
self.modelbuffer.addRenderTexture(self.texDepth,
|
||||
GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil)
|
||||
self.modelbuffer.addRenderTexture(self.texAlbedo,
|
||||
GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
|
||||
self.modelbuffer.addRenderTexture(self.texNormal,
|
||||
GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0)
|
||||
|
||||
self.lightbuffer.addRenderTexture(self.texFinal,
|
||||
GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
|
||||
|
||||
# Set the near and far clipping planes.
|
||||
|
||||
self.cam.node().getLens().setNear(50.0)
|
||||
self.cam.node().getLens().setFar(500.0)
|
||||
lens = self.cam.node().getLens()
|
||||
|
||||
# This algorithm uses three cameras: one to render the models into the
|
||||
# model buffer, one to render the lights into the light buffer, and
|
||||
# one to render "plain" stuff (non-deferred shaded) stuff into the
|
||||
# light buffer. Each camera has a bitmask to identify it.
|
||||
|
||||
self.modelMask = 1
|
||||
self.lightMask = 2
|
||||
self.plainMask = 4
|
||||
|
||||
self.modelcam = self.makeCamera(self.modelbuffer,
|
||||
lens=lens, scene=render, mask=self.modelMask)
|
||||
self.lightcam = self.makeCamera(self.lightbuffer,
|
||||
lens=lens, scene=render, mask=self.lightMask)
|
||||
self.plaincam = self.makeCamera(self.lightbuffer,
|
||||
lens=lens, scene=render, mask=self.plainMask)
|
||||
|
||||
# Panda's main camera is not used.
|
||||
|
||||
self.cam.node().setActive(0)
|
||||
|
||||
# Take explicit control over the order in which the three
|
||||
# buffers are rendered.
|
||||
|
||||
self.modelbuffer.setSort(1)
|
||||
self.lightbuffer.setSort(2)
|
||||
self.win.setSort(3)
|
||||
|
||||
# Within the light buffer, control the order of the two cams.
|
||||
|
||||
self.lightcam.node().getDisplayRegion(0).setSort(1)
|
||||
self.plaincam.node().getDisplayRegion(0).setSort(2)
|
||||
|
||||
# By default, panda usually clears the screen before every
|
||||
# camera and before every window. Tell it not to do that.
|
||||
# Then, tell it specifically when to clear and what to clear.
|
||||
|
||||
self.modelcam.node().getDisplayRegion(0).disableClears()
|
||||
self.lightcam.node().getDisplayRegion(0).disableClears()
|
||||
self.plaincam.node().getDisplayRegion(0).disableClears()
|
||||
self.cam.node().getDisplayRegion(0).disableClears()
|
||||
self.cam2d.node().getDisplayRegion(0).disableClears()
|
||||
self.modelbuffer.disableClears()
|
||||
self.win.disableClears()
|
||||
|
||||
self.modelbuffer.setClearColorActive(1)
|
||||
self.modelbuffer.setClearDepthActive(1)
|
||||
self.lightbuffer.setClearColorActive(1)
|
||||
self.lightbuffer.setClearColor((0, 0, 0, 1))
|
||||
|
||||
# Miscellaneous stuff.
|
||||
|
||||
self.disableMouse()
|
||||
self.camera.setPos(-9.112, -211.077, 46.951)
|
||||
self.camera.setHpr(0, -7.5, 2.4)
|
||||
random.seed()
|
||||
|
||||
# Calculate the projection parameters for the final shader.
|
||||
# The math here is too complex to explain in an inline comment,
|
||||
# I've put in a full explanation into the HTML intro.
|
||||
|
||||
proj = self.cam.node().getLens().getProjectionMat()
|
||||
proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0)
|
||||
proj_y = 0.5 * proj.getCell(3, 2)
|
||||
proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1)
|
||||
proj_w = -0.5 - 0.5 * proj.getCell(1, 2)
|
||||
|
||||
# Configure the render state of the model camera.
|
||||
|
||||
tempnode = NodePath(PandaNode("temp node"))
|
||||
tempnode.setAttrib(
|
||||
AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5))
|
||||
tempnode.setShader(loader.loadShader("model.sha"))
|
||||
tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual))
|
||||
self.modelcam.node().setInitialState(tempnode.getState())
|
||||
|
||||
# Configure the render state of the light camera.
|
||||
|
||||
tempnode = NodePath(PandaNode("temp node"))
|
||||
tempnode.setShader(loader.loadShader("light.sha"))
|
||||
tempnode.setShaderInput("texnormal", self.texNormal)
|
||||
tempnode.setShaderInput("texalbedo", self.texAlbedo)
|
||||
tempnode.setShaderInput("texdepth", self.texDepth)
|
||||
tempnode.setShaderInput("proj", (proj_x, proj_y, proj_z, proj_w))
|
||||
tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd,
|
||||
ColorBlendAttrib.OOne, ColorBlendAttrib.OOne))
|
||||
tempnode.setAttrib(
|
||||
CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise))
|
||||
# The next line causes problems on Linux.
|
||||
# tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual))
|
||||
tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
|
||||
self.lightcam.node().setInitialState(tempnode.getState())
|
||||
|
||||
# Configure the render state of the plain camera.
|
||||
|
||||
rs = RenderState.makeEmpty()
|
||||
self.plaincam.node().setInitialState(rs)
|
||||
|
||||
# Clear any render attribs on the root node. This is necessary
|
||||
# because by default, panda assigns some attribs to the root
|
||||
# node. These default attribs will override the
|
||||
# carefully-configured render attribs that we just attached
|
||||
# to the cameras. The simplest solution is to just clear
|
||||
# them all out.
|
||||
|
||||
render.setState(RenderState.makeEmpty())
|
||||
|
||||
# My artist created a model in which some of the polygons
|
||||
# don't have textures. This confuses the shader I wrote.
|
||||
# This little hack guarantees that everything has a texture.
|
||||
|
||||
white = loader.loadTexture("models/white.jpg")
|
||||
render.setTexture(white, 0)
|
||||
|
||||
# Create two subroots, to help speed cull traversal.
|
||||
|
||||
self.lightroot = NodePath(PandaNode("lightroot"))
|
||||
self.lightroot.reparentTo(render)
|
||||
self.modelroot = NodePath(PandaNode("modelroot"))
|
||||
self.modelroot.reparentTo(render)
|
||||
self.lightroot.hide(BitMask32(self.modelMask))
|
||||
self.modelroot.hide(BitMask32(self.lightMask))
|
||||
self.modelroot.hide(BitMask32(self.plainMask))
|
||||
|
||||
# Load the model of a forest. Make it visible to the model camera.
|
||||
# This is a big model, so we load it asynchronously while showing a
|
||||
# load text. We do this by passing in a callback function.
|
||||
self.loading = addTitle("Loading models...")
|
||||
|
||||
self.forest = NodePath(PandaNode("Forest Root"))
|
||||
self.forest.reparentTo(render)
|
||||
self.forest.hide(BitMask32(self.lightMask | self.plainMask))
|
||||
loader.loadModel([
|
||||
"models/background",
|
||||
"models/foliage01",
|
||||
"models/foliage02",
|
||||
"models/foliage03",
|
||||
"models/foliage04",
|
||||
"models/foliage05",
|
||||
"models/foliage06",
|
||||
"models/foliage07",
|
||||
"models/foliage08",
|
||||
"models/foliage09"],
|
||||
callback=self.finishLoading)
|
||||
|
||||
# Cause the final results to be rendered into the main window on a
|
||||
# card.
|
||||
|
||||
self.card = self.lightbuffer.getTextureCard()
|
||||
self.card.setTexture(self.texFinal)
|
||||
self.card.reparentTo(render2d)
|
||||
|
||||
# Panda contains a built-in viewer that lets you view the results of
|
||||
# your render-to-texture operations. This code configures the viewer.
|
||||
|
||||
self.bufferViewer.setPosition("llcorner")
|
||||
self.bufferViewer.setCardSize(0, 0.40)
|
||||
self.bufferViewer.setLayout("vline")
|
||||
self.toggleCards()
|
||||
self.toggleCards()
|
||||
|
||||
# Firefly parameters
|
||||
|
||||
self.fireflies = []
|
||||
self.sequences = []
|
||||
self.scaleseqs = []
|
||||
self.glowspheres = []
|
||||
self.fireflysize = 1.0
|
||||
self.spheremodel = loader.loadModel("misc/sphere")
|
||||
|
||||
# Create the firefly model, a fuzzy dot
|
||||
dotSize = 1.0
|
||||
cm = CardMaker("firefly")
|
||||
cm.setFrame(-dotSize, dotSize, -dotSize, dotSize)
|
||||
self.firefly = NodePath(cm.generate())
|
||||
self.firefly.setTexture(loader.loadTexture("models/firefly.png"))
|
||||
self.firefly.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add,
|
||||
ColorBlendAttrib.O_incoming_alpha, ColorBlendAttrib.O_one))
|
||||
|
||||
# these allow you to change parameters in realtime
|
||||
|
||||
self.accept("escape", sys.exit, [0])
|
||||
self.accept("arrow_up", self.incFireflyCount, [1.1111111])
|
||||
self.accept("arrow_down", self.decFireflyCount, [0.9000000])
|
||||
self.accept("arrow_right", self.setFireflySize, [1.1111111])
|
||||
self.accept("arrow_left", self.setFireflySize, [0.9000000])
|
||||
self.accept("v", self.toggleCards)
|
||||
self.accept("V", self.toggleCards)
|
||||
|
||||
def finishLoading(self, models):
|
||||
# This function is used as callback to loader.loadModel, and called
|
||||
# when all of the models have finished loading.
|
||||
|
||||
# Attach the models to the scene graph.
|
||||
for model in models:
|
||||
model.reparentTo(self.forest)
|
||||
|
||||
# Show the instructions.
|
||||
self.loading.destroy()
|
||||
self.title = addTitle("Panda3D: Tutorial - Fireflies using Deferred Shading")
|
||||
self.inst1 = addInstructions(0.06, "ESC: Quit")
|
||||
self.inst2 = addInstructions(0.12, "Up/Down: More / Fewer Fireflies (Count: unknown)")
|
||||
self.inst3 = addInstructions(0.18, "Right/Left: Bigger / Smaller Fireflies (Radius: unknown)")
|
||||
self.inst4 = addInstructions(0.24, "V: View the render-to-texture results")
|
||||
|
||||
self.setFireflySize(25.0)
|
||||
while len(self.fireflies) < 5:
|
||||
self.addFirefly()
|
||||
self.updateReadout()
|
||||
|
||||
self.nextadd = 0
|
||||
taskMgr.add(self.spawnTask, "spawner")
|
||||
|
||||
def makeFBO(self, name, auxrgba):
|
||||
# This routine creates an offscreen buffer. All the complicated
|
||||
# parameters are basically demanding capabilities from the offscreen
|
||||
# buffer - we demand that it be able to render to texture on every
|
||||
# bitplane, that it can support aux bitplanes, that it track
|
||||
# the size of the host window, that it can render to texture
|
||||
# cumulatively, and so forth.
|
||||
winprops = WindowProperties()
|
||||
props = FrameBufferProperties()
|
||||
props.setRgbColor(True)
|
||||
props.setRgbaBits(8, 8, 8, 8)
|
||||
props.setDepthBits(1)
|
||||
props.setAuxRgba(auxrgba)
|
||||
return self.graphicsEngine.makeOutput(
|
||||
self.pipe, "model buffer", -2,
|
||||
props, winprops,
|
||||
GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFCanBindEvery |
|
||||
GraphicsPipe.BFRttCumulative | GraphicsPipe.BFRefuseWindow,
|
||||
self.win.getGsg(), self.win)
|
||||
|
||||
def addFirefly(self):
|
||||
pos1 = LPoint3(random.uniform(-50, 50), random.uniform(-100, 150), random.uniform(-10, 80))
|
||||
dir = LVector3(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1))
|
||||
dir.normalize()
|
||||
pos2 = pos1 + (dir * 20)
|
||||
fly = self.lightroot.attachNewNode(PandaNode("fly"))
|
||||
glow = fly.attachNewNode(PandaNode("glow"))
|
||||
dot = fly.attachNewNode(PandaNode("dot"))
|
||||
color_r = 1.0
|
||||
color_g = random.uniform(0.8, 1.0)
|
||||
color_b = min(color_g, random.uniform(0.5, 1.0))
|
||||
fly.setColor(color_r, color_g, color_b, 1.0)
|
||||
fly.setShaderInput("lightcolor", color_r, color_g, color_b, 1.0)
|
||||
int1 = fly.posInterval(random.uniform(7, 12), pos1, pos2)
|
||||
int2 = fly.posInterval(random.uniform(7, 12), pos2, pos1)
|
||||
si1 = fly.scaleInterval(random.uniform(0.8, 1.5),
|
||||
LPoint3(0.2, 0.2, 0.2), LPoint3(0.2, 0.2, 0.2))
|
||||
si2 = fly.scaleInterval(random.uniform(1.5, 0.8),
|
||||
LPoint3(1.0, 1.0, 1.0), LPoint3(0.2, 0.2, 0.2))
|
||||
si3 = fly.scaleInterval(random.uniform(1.0, 2.0),
|
||||
LPoint3(0.2, 0.2, 0.2), LPoint3(1.0, 1.0, 1.0))
|
||||
siseq = Sequence(si1, si2, si3)
|
||||
siseq.loop()
|
||||
siseq.setT(random.uniform(0, 1000))
|
||||
seq = Sequence(int1, int2)
|
||||
seq.loop()
|
||||
self.spheremodel.instanceTo(glow)
|
||||
self.firefly.instanceTo(dot)
|
||||
glow.setScale(self.fireflysize * 1.1)
|
||||
glow.hide(BitMask32(self.modelMask | self.plainMask))
|
||||
dot.hide(BitMask32(self.modelMask | self.lightMask))
|
||||
dot.setColor(color_r, color_g, color_b, 1.0)
|
||||
self.fireflies.append(fly)
|
||||
self.sequences.append(seq)
|
||||
self.glowspheres.append(glow)
|
||||
self.scaleseqs.append(siseq)
|
||||
|
||||
def updateReadout(self):
|
||||
self.inst2.destroy()
|
||||
self.inst2 = addInstructions(0.12,
|
||||
"Up/Down: More / Fewer Fireflies (Currently: %d)" % len(self.fireflies))
|
||||
self.inst3.destroy()
|
||||
self.inst3 = addInstructions(0.18,
|
||||
"Right/Left: Bigger / Smaller Fireflies (Radius: %d ft)" % self.fireflysize)
|
||||
|
||||
def toggleCards(self):
|
||||
self.bufferViewer.toggleEnable()
|
||||
# When the cards are not visible, I also disable the color clear.
|
||||
# This color-clear is actually not necessary, the depth-clear is
|
||||
# sufficient for the purposes of the algorithm.
|
||||
if (self.bufferViewer.isEnabled()):
|
||||
self.modelbuffer.setClearColorActive(True)
|
||||
else:
|
||||
self.modelbuffer.setClearColorActive(False)
|
||||
|
||||
def incFireflyCount(self, scale):
|
||||
n = int((len(self.fireflies) * scale) + 1)
|
||||
while (n > len(self.fireflies)):
|
||||
self.addFirefly()
|
||||
self.updateReadout()
|
||||
|
||||
def decFireflyCount(self, scale):
|
||||
n = int(len(self.fireflies) * scale)
|
||||
if (n < 1):
|
||||
n = 1
|
||||
while (len(self.fireflies) > n):
|
||||
self.glowspheres.pop()
|
||||
self.sequences.pop().finish()
|
||||
self.scaleseqs.pop().finish()
|
||||
self.fireflies.pop().removeNode()
|
||||
self.updateReadout()
|
||||
|
||||
def setFireflySize(self, n):
|
||||
n = n * self.fireflysize
|
||||
self.fireflysize = n
|
||||
for x in self.glowspheres:
|
||||
x.setScale(self.fireflysize * 1.1)
|
||||
self.updateReadout()
|
||||
|
||||
def spawnTask(self, task):
|
||||
if task.time > self.nextadd:
|
||||
self.nextadd = task.time + 1.0
|
||||
if (len(self.fireflies) < 300):
|
||||
self.incFireflyCount(1.03)
|
||||
return Task.cont
|
||||
|
||||
demo = FireflyDemo()
|
||||
demo.run()
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//Cg
|
||||
//
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float2 vtx_texcoord0 : TEXCOORD0,
|
||||
float4 vtx_normal : NORMAL,
|
||||
float4 vtx_color : COLOR,
|
||||
out float4 l_position : POSITION,
|
||||
out float2 l_texcoord0 : TEXCOORD0,
|
||||
out float4 l_color : COLOR,
|
||||
out float3 l_normal : TEXCOORD1,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 itp_modelview)
|
||||
{
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_texcoord0 = vtx_texcoord0;
|
||||
l_color = vtx_color;
|
||||
l_normal = (float3)mul(itp_modelview, vtx_normal);
|
||||
}
|
||||
|
||||
void fshader(float2 l_texcoord0: TEXCOORD0,
|
||||
float4 l_color: COLOR,
|
||||
float3 l_normal: TEXCOORD1,
|
||||
uniform sampler2D tex_0 : TEXUNIT0,
|
||||
out float4 o_color: COLOR0,
|
||||
out float4 o_normal: COLOR1)
|
||||
{
|
||||
l_normal = normalize(l_normal);
|
||||
o_color = l_color * tex2D(tex_0, l_texcoord0);
|
||||
o_normal.rgb = (l_normal * 0.5) + float3(0.5, 0.5, 0.5);
|
||||
o_normal.a = o_color.a;
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 22 KiB |