Official files

This commit is contained in:
ToontownStride 2015-11-14 14:28:53 -05:00
parent b7aa710d9f
commit 23139d101d
11418 changed files with 2635555 additions and 0 deletions

3
build/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
libpandadna/
built/
*.pdb

72
build/data/NiraiStart.py Normal file
View file

@ -0,0 +1,72 @@
from panda3d.core import *
import __builtin__, os, sys
import aes
import niraidata
# Config
prc = niraidata.CONFIG
iv, key, prc = prc[:16], prc[16:32], prc[32:]
prc = aes.decrypt(prc, key, iv)
for line in prc.split('\n'):
line = line.strip()
if line:
loadPrcFileData('nirai config', line)
del prc
del iv
del key
# DC
__builtin__.dcStream = StringStream()
dc = niraidata.DC
iv, key, dc = dc[:16], dc[16:32], dc[32:]
dc = aes.decrypt(dc, key, iv)
dcStream.setData(dc)
del dc
del iv
del key
# Resources
# TO DO: Sign and verify the phases to prevent editing.
vfs = VirtualFileSystem.getGlobalPtr()
mfs = [3, 3.5, 4, 5, 5.5, 6, 7, 8, 9, 10, 11, 12, 13]
abort = False
for mf in mfs:
filename = 'resources/default/phase_%s.mf' % mf
if not os.path.isfile(filename):
print 'Phase %s not found' % filename
abort = True
break
mf = Multifile()
mf.openRead(filename)
if not vfs.mount(mf, '/', 0):
print 'Unable to mount %s' % filename
abort = True
break
# Packs
pack = os.environ.get('TT_STRIDE_CONTENT_PACK')
import glob
if pack and pack != 'default':
print 'Loading content pack', pack
for file in glob.glob('resources/%s/*.mf' % pack):
mf = Multifile()
mf.openReadWrite(Filename(file))
names = mf.getSubfileNames()
for name in names:
ext = os.path.splitext(name)[1]
if ext not in ['.jpg', '.jpeg', '.ogg', '.rgb']:
mf.removeSubfile(name)
vfs.mount(mf, Filename('/'), 0)
if not abort:
# Run
import toontown.toonbase.ToontownStart

3
build/linux/aes-to-so.sh Normal file
View file

@ -0,0 +1,3 @@
gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/include -I/usr/include/python2.7 -lstdc++ -lssl -lcrypto -c ../nirai/src/aes.cxx -c -o ../nirai/src/aes.o
gcc -shared ../nirai/src/aes.o -L/usr/local/lib -lstdc++ -lssl -lcrypto -o ../nirai/src/aes.so

View file

@ -0,0 +1,2 @@
cd ../nirai/panda3d
python2.7 makepanda/makepanda.py --everything --no-contrib --no-fmodex --no-physx --no-bullet --no-pview --no-pandatool --no-swscale --no-swresample --no-speedtree --no-vrpn --no-artoolkit --no-opencv --no-directcam --no-vision --no-rocket --no-awesomium --no-deploytools --no-skel --no-ffmpeg --no-eigen --static

141
build/make.py Normal file
View file

@ -0,0 +1,141 @@
from panda3d.core import *
import argparse, struct
import sys, glob
import os
sys.path.append('nirai/src')
from niraitools import *
parser = argparse.ArgumentParser()
parser.add_argument('--compile-cxx', '-c', action='store_true',
help='Compile the CXX codes and generate stride.exe into built.')
parser.add_argument('--make-nri', '-n', action='store_true',
help='Generate stride NRI.')
parser.add_argument('--make-mfs', '-m', action='store_true',
help='Make multifiles')
args = parser.parse_args()
if not os.path.exists('built'):
os.mkdir('built')
def niraicall_obfuscate(code):
# We'll obfuscate if len(code) % 4 == 0
# This way we make sure both obfuscated and non-obfuscated code work.
if len(code) % 4:
return False, None
# Reverse
code = code[::-1]
# XOR
key = ['B', 'A', 'Q', 'J', 'R', 'P', 'Z', 'P', 'A', 'H', 'U', 'T']
output = []
for i in range(len(code)):
xor_num = ord(code[i]) ^ ord(key[i % len(key)])
output.append(chr(xor_num))
code = ''.join(output)
return True, code
niraimarshal.niraicall_obfuscate = niraicall_obfuscate
class StridePackager(NiraiPackager):
HEADER = 'TTSTRIDE'
BASEDIR = '..' + os.sep
def __init__(self, outfile, configPath=None):
NiraiPackager.__init__(self, outfile)
self.__manglebase = self.get_mangle_base(self.BASEDIR)
self.add_panda3d_dirs()
self.add_default_lib()
self.globalConfigPath = configPath
def add_source_dir(self, dir):
self.add_directory(self.BASEDIR + dir, mangler=self.__mangler)
def add_data_file(self, file):
mb = self.get_mangle_base('data/')
self.add_file('data/%s.py' % file, mangler=lambda x: x[mb:])
def __mangler(self, name):
if name.endswith('AI') or name.endswith('UD') or name in ('ToontownAIRepository', 'ToontownUberRepository',
'ToontownInternalRepository', 'ServiceStart'):
if not 'NonRepeatableRandomSource' in name:
return ''
return name[self.__manglebase:].strip('.')
def generate_niraidata(self):
print 'Generating niraidata'
if self.globalConfigPath is not None:
config = self.get_file_contents(self.globalConfigPath)
else:
config = self.get_file_contents('../dependencies/config/general.prc')
config += '\n\n' + self.get_file_contents('../dependencies/config/release/qa.prc')
config_iv = self.generate_key(16)
config_key = self.generate_key(16)
config = config_iv + config_key + aes.encrypt(config, config_key, config_iv)
niraidata = 'CONFIG = %r' % config
# DC
niraidata += '\nDC = %r' % self.get_file_contents('../dependencies/astron/dclass/stride.dc', True)
self.add_module('niraidata', niraidata, compile=True)
def process_modules(self):
# TODO: Compression
dg = Datagram()
dg.addUint32(len(self.modules))
for moduleName in self.modules:
data, size = self.modules[moduleName]
dg.addString(moduleName)
dg.addInt32(size)
dg.appendData(data)
data = dg.getMessage()
iv = self.generate_key(16)
key = self.generate_key(16)
fixed_key = ''.join(chr((i ^ (7 * i + 16)) % ((i + 5) * 3)) for i in xrange(16))
fixed_iv = ''.join(chr((i ^ (2 * i + 53)) % ((i + 9) * 6)) for i in xrange(16))
securekeyandiv = aes.encrypt(iv + key, fixed_key, fixed_iv)
return securekeyandiv + aes.encrypt(data, key, iv)
# Compile the engine
if args.compile_cxx:
compiler = NiraiCompiler('stride.exe', libs=set(glob.glob('libpandadna/libpandadna.dir/Release/*.obj')))
compiler.add_nirai_files()
compiler.add_source('src/stride.cxx')
compiler.run()
# Compile the game data
if args.make_nri:
pkg = StridePackager('built/TTSData.bin')
pkg.add_source_dir('otp')
pkg.add_source_dir('toontown')
pkg.add_data_file('NiraiStart')
pkg.generate_niraidata()
pkg.write_out()
if args.make_mfs:
os.chdir('../resources')
cmd = ''
for phasenum in ['3', '3.5', '4', '5', '5.5', '6', '7', '8', '9', '10', '11', '12', '13']:
print 'phase_%s' % (phasenum)
cmd = 'multify -cf ../build/built/resources/default/phase_%s.mf phase_%s' % (phasenum, phasenum)
p = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
v = p.wait()
if v != 0:
print 'The following command returned non-zero value (%d): %s' % (v, cmd[:100] + '...')
sys.exit(1)

4
build/nirai/panda3d/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/built_x64
/built
/thirdparty
/targetroot

View file

@ -0,0 +1,15 @@
language: cpp
compiler:
- gcc
- clang
before_script:
- sudo apt-get install python-dev libpng-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libjpeg-dev libopenal-dev libode-dev nvidia-cg-toolkit
script: python makepanda/makepanda.py --everything --verbose --git-commit $TRAVIS_COMMIT --installer --threads 2
notifications:
irc:
channels:
- "chat.freenode.net#panda3d"
on_success: change
on_failure: always
use_notice: true
skip_join: true

View file

@ -0,0 +1,30 @@
Copyright (c) 2008, Carnegie Mellon University.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Carnegie Mellon University nor the names of
other contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(This is the Modified BSD License. See also
http://www.opensource.org/licenses/bsd-license.php )

View file

@ -0,0 +1,47 @@
Nirai's Panda3D
=======
Panda3D is a game engine, a framework for 3D rendering and game development for
Python and C++ programs. Panda3D is open-source and free for any purpose,
including commercial ventures, thanks to its
[liberal license](https://www.panda3d.org/license.php). To learn more about
Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery.php)
and the [feature list](https://www.panda3d.org/features.php). To learn how to
use Panda3D, check the [documentation](https://www.panda3d.org/documentation.php)
resources. If you get stuck, ask for help from our active
[community](https://www.panda3d.org/community.php).
Panda3D is licensed under the Modified BSD License. See the LICENSE file for
more details.
Building Panda3D
================
Windows
-------
We currently build using the Microsoft Visual C++ 2010 compiler. You do not
need Microsoft Visual Studio to build Panda3D, though - the relevant compilers
are included as part of the Windows 7.1 SDK.
You will also need to have the third-party dependency libraries available for
the build scripts to use. These are available from Nirai organization.
After acquiring these dependencies, you may simply build Panda3D from the
command prompt using the following command:
```bash
compile.bat
postbuild.bat
```
_postbuild_ cleans up _built_ dir.
Linux
-----
TBA
Mac OS X
--------
TBA

View file

@ -0,0 +1,2 @@
@echo off
thirdparty\win-python\python makepanda/makepanda.py --everything --no-contrib --no-fmodex --no-physx --no-bullet --no-pview --no-pandatool --no-swscale --no-swresample --no-speedtree --no-vrpn --no-artoolkit --no-opencv --no-directcam --no-vision --no-rocket --no-awesomium --no-deploytools --no-skel --no-ffmpeg --no-eigen --static %*

View file

@ -0,0 +1,9 @@
*.pyc
*.pyo
/__init__.py
# These are files that are generated within the source tree by the
# ppremake system.
Makefile
pp.dep
/built/
Opt?-*

View file

@ -0,0 +1,60 @@
//
// Package.pp
//
// This file defines certain configuration variables that are to be
// written into the various make scripts. It is processed by ppremake
// (along with the Sources.pp files in each of the various
// contribories) to generate build scripts appropriate to each
// environment.
//
// This is the package-specific file, which should be at the top of
// every source hierarchy. It generally gets the ball rolling, and is
// responsible for explicitly including all of the relevent Config.pp
// files.
// What is the name and version of this source tree?
#if $[eq $[PACKAGE],]
#define PACKAGE contrib
#define VERSION 0.80
#endif
// Where should we find the PANDA source contribory?
#if $[PANDA_SOURCE]
#define PANDA_SOURCE $[unixfilename $[PANDA_SOURCE]]
#elif $[or $[CTPROJS],$[PANDA]]
// If we are presently attached, use the environment variable.
#define PANDA_SOURCE $[unixfilename $[PANDA]]
#if $[eq $[PANDA],]
#error You seem to be attached to some trees, but not PANDA!
#endif
#else
// Otherwise, if we are not attached, we guess that the source is a
// sibling contribory to this source root.
#define PANDA_SOURCE $[standardize $[TOPDIR]/../panda]
#endif
// Where should we install CONTRIB?
#if $[CONTRIB_INSTALL]
#define CONTRIB_INSTALL $[unixfilename $[CONTRIB_INSTALL]]
#elif $[CTPROJS]
#set CONTRIB $[unixfilename $[CONTRIB]]
#define CONTRIB_INSTALL $[CONTRIB]/built
#if $[eq $[CONTRIB],]
#error You seem to be attached to some trees, but not CONTRIB!
#endif
#else
#defer CONTRIB_INSTALL $[unixfilename $[INSTALL_DIR]]
#endif
// Also get the PANDA Package file and everything that includes.
#if $[not $[isfile $[PANDA_SOURCE]/Package.pp]]
#printvar PANDA_SOURCE
#error PANDA source contribory not found from contrib! Are you attached properly?
#endif
#include $[PANDA_SOURCE]/Package.pp
// Define the inter-tree dependencies.
#define NEEDS_TREES panda $[NEEDS_TREES]
#define DEPENDABLE_HEADER_DIRS $[DEPENDABLE_HEADER_DIRS] $[PANDA_INSTALL]/include

View file

@ -0,0 +1,10 @@
// This is the toplevel directory for a package.
#define DIR_TYPE toplevel
#define REQUIRED_TREES dtool panda
#define EXTRA_DIST \
Sources.pp Config.pp Package.pp
#define PYTHON_PACKAGE 1

View file

@ -0,0 +1,4 @@
// This is a group directory: a directory level above a number of
// source subdirectories.
#define DIR_TYPE group

View file

@ -0,0 +1,82 @@
#define LOCAL_LIBS p3contribbase
#define BUILDING_DLL BUILDING_PANDAAI
#define OTHER_LIBS \
panda:c \
p3express:c p3putil:c p3pandabase:c pandaexpress:m \
p3interrogatedb:c p3prc:c p3dconfig:c p3dtoolconfig:m \
p3dtoolutil:c p3dtoolbase:c p3dtool:m
#begin lib_target
#define TARGET pandaai
#define COMBINED_SOURCES p3ai_composite1.cxx
#define SOURCES \
aiBehaviors.h \
aiCharacter.h \
aiGlobals.h \
aiNode.h \
aiPathFinder.h \
aiWorld.h \
arrival.h \
config_ai.h \
evade.h \
flee.h \
flock.h \
aiGlobals.h \
meshNode.h \
obstacleAvoidance.h \
pathFind.h \
pathFollow.h \
pursue.h \
seek.h \
wander.h
#define INCLUDED_SOURCES \
aiBehaviors.cxx \
aiCharacter.cxx \
aiNode.cxx \
aiPathFinder.cxx \
aiWorld.cxx \
p3ai_composite.cxx \
p3ai_composite1.cxx \
arrival.cxx \
config_ai.cxx \
evade.cxx \
flee.cxx \
flock.cxx \
meshNode.cxx \
obstacleAvoidance.cxx \
pathFind.cxx \
pathFollow.cxx \
pursue.cxx \
seek.cxx \
wander.cxx
#define INSTALL_HEADERS \
aiBehaviors.h \
aiCharacter.h \
aiGlobals.h \
aiNode.h \
aiPathFinder.h \
aiWorld.h \
arrival.h \
config_ai.h \
evade.h \
flee.h \
flock.h \
aiGlobals.h \
meshNode.h \
obstacleAvoidance.h \
pathFind.h \
pathFollow.h \
pursue.h \
seek.h \
wander.h
#define IGATESCAN all
#end lib_target

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,187 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiBehaviors.cxx
// Created by : Deepak, John, Navin
// Date : 8 Sep 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#pragma warning (disable:4996)
#pragma warning (disable:4005)
#pragma warning(disable:4275)
#ifndef _AIBEHAVIORS_H
#define _AIBEHAVIORS_H
#include "aiGlobals.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Class : AIBehaviors
// Description : This class implements all the steering behaviors of the AI framework, such as
// seek, flee, pursue, evade, wander and flock. Each steering behavior has a weight which is used when more than
// one type of steering behavior is acting on the same ai character. The weight decides the contribution of each
// type of steering behavior. The AICharacter class has a handle to an object of this class and this allows to
// invoke the steering behaviors via the AICharacter. This class also provides functionality such as pausing, resuming
// and removing the AI behaviors of an AI character at anytime.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class AICharacter;
class Seek;
class Flee;
class Pursue;
class Evade;
class Arrival;
class Flock;
class Wander;
class PathFollow;
class PathFind;
class ObstacleAvoidance;
typedef list<Flee, allocator<Flee> > ListFlee;
typedef list<Evade, allocator<Evade> > ListEvade;
class EXPCL_PANDAAI AIBehaviors {
public:
enum _behavior_type {
_none = 0x00000,
_seek = 0x00002,
_flee = 0x00004,
_flee_activate = 0x00100,
_arrival = 0x00008,
_arrival_activate = 0x01000,
_wander = 0x00010,
_pursue = 0x00040,
_evade = 0x00080,
_evade_activate = 0x00800,
_flock = 0x00200,
_flock_activate = 0x00400,
_obstacle_avoidance = 0x02000,
_obstacle_avoidance_activate = 0x04000
};
AICharacter *_ai_char;
Flock *_flock_group;
int _behaviors_flags;
LVecBase3 _steering_force;
Seek *_seek_obj;
LVecBase3 _seek_force;
Flee *_flee_obj;
LVecBase3 _flee_force;
//! This list is used if the ai character needs to flee from multiple onjects.
ListFlee _flee_list;
ListFlee::iterator _flee_itr;
Pursue *_pursue_obj;
LVecBase3 _pursue_force;
Evade *_evade_obj;
LVecBase3 _evade_force;
//! This list is used if the ai character needs to evade from multiple onjects.
ListEvade _evade_list;
ListEvade::iterator _evade_itr;
Arrival *_arrival_obj;
LVecBase3 _arrival_force;
//! Since Flock is a collective behavior the variables are declared within the AIBehaviors class.
float _flock_weight;
LVecBase3 _flock_force;
bool _flock_done;
Wander * _wander_obj;
LVecBase3 _wander_force;
ObstacleAvoidance *_obstacle_avoidance_obj;
LVecBase3 _obstacle_avoidance_force;
PathFollow *_path_follow_obj;
PathFind *_path_find_obj;
bool _conflict, _previous_conflict;
AIBehaviors();
~AIBehaviors();
bool is_on(_behavior_type bt);
bool is_on(string ai_type); // special cases for pathfollow and pathfinding
bool is_off(_behavior_type bt);
bool is_off(string ai_type); // special cases for pathfollow and pathfinding
void turn_on(string ai_type);
void turn_off(string ai_type);
bool is_conflict();
void accumulate_force(string force_type, LVecBase3 force);
LVecBase3 calculate_prioritized();
void flock_activate();
LVecBase3 do_flock();
int char_to_int(string ai_type);
PUBLISHED:
void seek(NodePath target_object, float seek_wt = 1.0);
void seek(LVecBase3 pos, float seek_wt = 1.0);
void flee(NodePath target_object, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0);
void flee(LVecBase3 pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0);
void pursue(NodePath target_object, float pursue_wt = 1.0);
void evade(NodePath target_object, double panic_distance = 10.0, double relax_distance = 10.0, float evade_wt = 1.0);
void arrival(double distance = 10.0);
void flock(float flock_wt);
void wander(double wander_radius = 5.0, int flag =0, double aoe = 0.0, float wander_weight = 1.0);
void obstacle_avoidance(float feeler_length = 1.0);
void path_follow(float follow_wt);
void add_to_path(LVecBase3 pos);
void start_follow(string type = "normal");
// should have different function names.
void init_path_find(const char* navmesh_filename);
void path_find_to(LVecBase3 pos, string type = "normal");
void path_find_to(NodePath target, string type = "normal");
void add_static_obstacle(NodePath obstacle);
void add_dynamic_obstacle(NodePath obstacle);
//
void remove_ai(string ai_type);
void pause_ai(string ai_type);
void resume_ai(string ai_type);
string behavior_status(string ai_type);
};
#endif

View file

@ -0,0 +1,124 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiCharacter.cxx
// Created by : Deepak, John, Navin
// Date : 8 Sep 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "aiCharacter.h"
AICharacter::AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force) {
_name = model_name;
_ai_char_np = model_np;
_mass = mass;
_max_force = max_force;
_movt_force = movt_force;
_velocity = LVecBase3(0.0, 0.0, 0.0);
_steering_force = LVecBase3(0.0, 0.0, 0.0);
_steering = new AIBehaviors();
_steering->_ai_char = this;
_pf_guide = false;
}
AICharacter::~AICharacter() {
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : update
// Description : Each character's update will update its ai and physics
// based on his resultant steering force.
// This also makes the character look at the direction of the force.
/////////////////////////////////////////////////////////////////////////////////////////
void AICharacter::
update() {
if (!_steering->is_off(_steering->_none)) {
LVecBase3 old_pos = _ai_char_np.get_pos();
LVecBase3 steering_force = _steering->calculate_prioritized();
LVecBase3 acceleration = steering_force / _mass;
_velocity = acceleration;
LVecBase3 direction = _steering->_steering_force;
direction.normalize();
_ai_char_np.set_pos(old_pos + _velocity) ;
if (steering_force.length() > 0) {
_ai_char_np.look_at(old_pos + (direction * 5));
_ai_char_np.set_h(_ai_char_np.get_h() + 180);
_ai_char_np.set_p(-_ai_char_np.get_p());
_ai_char_np.set_r(-_ai_char_np.get_r());
}
} else {
_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_seek_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_flee_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_evade_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_flock_force = LVecBase3(0.0, 0.0, 0.0);
_steering->_wander_force = LVecBase3(0.0, 0.0, 0.0);
}
}
LVecBase3 AICharacter::get_velocity() {
return _velocity;
}
void AICharacter::set_velocity(LVecBase3 velocity) {
_velocity = velocity;
}
double AICharacter::get_mass() {
return _mass;
}
void AICharacter::set_mass(double m) {
_mass = m;
}
double AICharacter::get_max_force() {
return _max_force;
}
void AICharacter::set_max_force(double max_force) {
_max_force = max_force;
}
NodePath AICharacter::get_node_path() {
return _ai_char_np;
}
void AICharacter::set_node_path(NodePath np) {
_ai_char_np = np;
}
AIBehaviors * AICharacter::get_ai_behaviors() {
return _steering;
}
void AICharacter::set_char_render(NodePath render) {
_window_render = render;
}
NodePath AICharacter::get_char_render() {
return _window_render;
}
void AICharacter::set_pf_guide(bool pf_guide) {
_pf_guide = pf_guide;
}

View file

@ -0,0 +1,79 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiCharacter.h
// Created by : Deepak, John, Navin
// Date : 8 Sep 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#pragma warning (disable:4996)
#pragma warning (disable:4005)
#pragma warning(disable:4275)
#ifndef _AICHARACTER_H
#define _AICHARACTER_H
#include "aiBehaviors.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Class : AICharacter
// Description : This class is used for creating the ai characters. It assigns both physics and ai
// attributes to the character. It also has an update function which updates the physics and ai
// of the character. This update function is called by the AIWorld update.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class AIBehaviors;
class AIWorld;
class EXPCL_PANDAAI AICharacter {
public:
double _mass;
double _max_force;
LVecBase3 _velocity;
LVecBase3 _steering_force;
string _name;
double _movt_force;
unsigned int _ai_char_flock_id;
AIWorld *_world;
AIBehaviors *_steering;
NodePath _window_render;
NodePath _ai_char_np;
bool _pf_guide;
void update();
void set_velocity(LVecBase3 vel);
void set_char_render(NodePath render);
NodePath get_char_render();
PUBLISHED:
double get_mass();
void set_mass(double m);
LVecBase3 get_velocity();
double get_max_force();
void set_max_force(double max_force);
NodePath get_node_path();
void set_node_path(NodePath np);
AIBehaviors * get_ai_behaviors();
// This function is used to enable or disable the guides for path finding.
void set_pf_guide(bool pf_guide);
AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force);
~AICharacter();
};
#endif

View file

@ -0,0 +1,35 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiGlobals.h
// Created by : Deepak, John, Navin
// Date: 8 Sep 09
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#pragma warning (disable:4996)
#pragma warning (disable:4005)
#pragma warning(disable:4275)
#ifndef _AI_GLOBALS_H
#define _AI_GLOBALS_H
#include "config_ai.h"
#include "pandaFramework.h"
#include "textNode.h"
#include "pandaSystem.h"
#include "lvecBase3.h"
#include "nodePath.h"
#include "genericAsyncTask.h"
#include "asyncTaskManager.h"
#endif

View file

@ -0,0 +1,53 @@
// Filename: aiNode.cxx
// Created by: Deepak, John, Navin (19Nov2009)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "aiNode.h"
AINode::AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) {
for (int i = 0; i < 8; ++i) {
_neighbours[i] = NULL;
}
_position = pos;
_width = w;
_length = l;
_height = h;
_grid_x = grid_x;
_grid_y = grid_y;
_status = ST_neutral;
_type = true;
_score = 0;
_cost = 0;
_heuristic = 0;
_next = NULL;
_prv_node = NULL;
}
AINode::~AINode() {
}
////////////////////////////////////////////////////////////////////
// Function: contains
// Description: This is a handy function which returns true if the
// passed position is within the node's dimensions.
////////////////////////////////////////////////////////////////////
bool AINode::contains(float x, float y) {
if (_position.get_x() - _width / 2 <= x && _position.get_x() + _width / 2 >= x &&
_position.get_y() - _length / 2 <= y && _position.get_y() + _length / 2 >= y) {
return true;
}
else {
return false;
}
}

View file

@ -0,0 +1,86 @@
// Filename: aiNode.h
// Created by: Deepak, John, Navin (18Nov2009)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef AINODE_H
#define AINODE_H
#include "aiGlobals.h"
////////////////////////////////////////////////////////////////////
// Class : AINode
// Description : This class is used to assign the nodes on the mesh.
// It holds all the data necessary to compute A*
// algorithm. It also maintains a lot of vital
// information such as the neighbor nodes of each
// node and also its position on the mesh.
// Note: The Mesh Generator which is a standalone
// tool makes use of this class to generate the nodes
// on the mesh.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI AINode {
public:
// This variable specifies the node status whether open, close
// or neutral.
// open = belongs to _open_list.
// close = belongs to _closed_list.
// neutral = unexamined node.
enum Status {
ST_open,
ST_close,
ST_neutral
};
Status _status;
// This variable specifies whether the node is an obtacle or not.
// Used for dynamic obstacle addition to the environment.
// obstacle = false
// navigational = true
bool _type;
// The score is used to compute the traversal expense to nodes
// when using A*.
// _score = _cost + heuristic
int _score;
int _cost;
int _heuristic;
// Used to trace back the path after it is generated using A*.
AINode *_prv_node;
// Position of the node in the 2d grid.
int _grid_x, _grid_y;
// Position of the node in 3D space.
LVecBase3 _position;
// Dimensions of each face / cell on the mesh.
// Height is given in case of expansion to a 3d mesh. Currently
// not used.
float _width, _length ,_height;
AINode *_neighbours[8]; // anti-clockwise from top left corner.
// The _next pointer is used for traversal during mesh
// generation from the model.
// Note: The data in this member is discarded when mesh data
// is written into navmesh.csv file.
AINode *_next;
PUBLISHED:
AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
~AINode();
bool contains(float x, float y);
};
#endif

View file

@ -0,0 +1,395 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiPathFinder.cxx
// Created by : Deepak, John, Navin
// Date : 10 Nov 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "aiPathFinder.h"
PathFinder::PathFinder(NavMesh nav_mesh) {
_grid = nav_mesh;
}
PathFinder::~PathFinder() {
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : find_path
// Description : This function initializes the pathfinding process by accepting the
// source and destination nodes. It then calls the generate_path().
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::find_path(Node *src_node, Node *dest_node) {
_src_node = src_node;
_dest_node = dest_node;
// Add a dummy node as the first element of the open list with score = -1.
// Inorder to implement a binary heap the index of the elements should never be 0.
Node *_dummy_node = new Node(-1, -1, LVecBase3(0.0, 0.0, 0.0), 0, 0, 0);
_dummy_node->_status = _dummy_node->open;
_dummy_node->_score = -1;
_open_list.push_back(_dummy_node);
// Add the source node to the open list.
add_to_olist(_src_node);
// Generate the path.
generate_path();
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : generate_path
// Description : This function performs the pathfinding process using the A* algorithm.
// It updates the openlist and closelist.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::generate_path() {
// All the A* algorithm is implemented here.
// The check is > 1 due to the existence of the dummy node.
while(_open_list.size() > 1) {
// The first element of the open list will always be the optimal node.
// This is because the open list is a binary heap with element having the
// smallest score at the top of the heap.
Node* nxt_node = _open_list[1];
if(nxt_node->_grid_x == _dest_node->_grid_x &&
nxt_node->_grid_y == _dest_node->_grid_y) {
// Remove the optimal node from the top of the heap.
remove_from_olist();
// add the used node to the closed list.
add_to_clist(nxt_node);
// At this point the destination is reached.
return;
}
else {
identify_neighbors(nxt_node);
// add the used node to the closed list.
add_to_clist(nxt_node);
}
}
cout<<"DESTINATION NOT REACHABLE MATE!"<<endl;
_closed_list.clear();
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : identify_neighbors
// Description : This function traverses through the 8 neigbors of the parent node and
// then adds the neighbors to the _open_list based on A* criteria.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::identify_neighbors(Node *parent_node) {
// Remove the parent node from the open_list so that it is not considered
// while adding new nodes to the open list heap.
remove_from_olist();
for(int i = 0; i < 8; ++i) {
if(parent_node->_neighbours[i] != NULL) {
if(parent_node->_neighbours[i]->_status == parent_node->_neighbours[i]->neutral
&& parent_node->_neighbours[i]->_type == true) {
// Link the neighbor to the parent node.
parent_node->_neighbours[i]->_prv_node = parent_node;
// Calculate and update the score for the node.
calc_node_score(parent_node->_neighbours[i]);
// Add the neighbor to the open list.
add_to_olist(parent_node->_neighbours[i]);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : calc_node_score
// Description : This function calculates the score of each node.
// Score = Cost + Heuristics.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::calc_node_score(Node *nd) {
nd->_cost = calc_cost_frm_src(nd);
nd->_heuristic = calc_heuristic(nd);
nd->_score = nd->_cost + nd->_heuristic;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : calc_cost_frm_src
// Description : This function calculates the cost of each node by finding out
// the number of node traversals required to reach the source node.
// Diagonal traversals have cost = 14.
// Horizontal / Vertical traversals have cost = 10.
/////////////////////////////////////////////////////////////////////////////////////////
int PathFinder::calc_cost_frm_src(Node *nd) {
int cost = 0;
Node *start_node = nd;
while(start_node->_prv_node != _src_node) {
if(is_diagonal_node(start_node)) {
cost += 14;
}
else {
cost += 10;
}
start_node = start_node->_prv_node;
}
// Add the cost of traversal to the source node also.
if(is_diagonal_node(start_node)) {
cost += 14;
}
else {
cost += 10;
}
return cost;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : calc_heuristic
// Description : This function calculates the heuristic of the nodes using Manhattan method.
// All it does is predict the number of node traversals required to reach the target node.
// No diagonal traversals are allowed in this technique.
/////////////////////////////////////////////////////////////////////////////////////////
int PathFinder::calc_heuristic(Node *nd) {
int row_diff = abs(_dest_node->_grid_x - nd->_grid_x);
int col_diff = abs(_dest_node->_grid_y - nd->_grid_y);
int heuristic = 10 * (row_diff + col_diff);
return heuristic;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : is_diagonal_node
// Description : This function checks if the traversal from a node is diagonal.
/////////////////////////////////////////////////////////////////////////////////////////
bool PathFinder::is_diagonal_node(Node *nd) {
// Calculate the row and column differences between child and parent nodes.
float row_diff = nd->_grid_x - nd->_prv_node->_grid_x;
float col_diff = nd->_grid_y - nd->_prv_node->_grid_y;
// Check if the relationship between child and parent node is diagonal.
if(row_diff == 0 || col_diff == 0) {
return false;
}
else {
return true;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : add_to_olist
// Description : This function adds a node to the open list heap.
// A binay heap is maintained to improve the search.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::add_to_olist(Node *nd) {
// Variables required to search the binary heap.
Node *child_node, *parent_node;
int child_idx, parent_idx;
// Set the status as open.
nd->_status = nd->open;
// Add the node to the open list.
_open_list.push_back(nd);
// Find the parent and child nodes and create temporary nodes out of them.
// In a binary heap the children of a parent node are always i*2 and i*2 + 1,
// where i is the index of the parent node in the heap. And hence, the parent
// of a node can be easily found out by dividing by 2 and rounding it.
child_idx = _open_list.size() - 1;
parent_idx = child_idx / 2;
child_node = _open_list[child_idx];
parent_node = _open_list[parent_idx];
// Keep traversing the heap until the lowest element in the list is bubbled
// to the top of the heap.
while(_open_list[child_idx]->_score <= _open_list[parent_idx]->_score) {
// Swap the parent node and the child node.
_open_list[parent_idx] = child_node;
_open_list[child_idx] = parent_node;
// Update the new child and parent indices.
child_idx = parent_idx;
parent_idx = child_idx / 2;
// Update the new child and parent nodes.
child_node = _open_list[child_idx];
parent_node = _open_list[parent_idx];
}
// At this point the Node with the smallest score will be at the top of the heap.
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : remove_from_olist
// Description : This function removes a node from the open list.
// During the removal the binary heap is maintained.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::remove_from_olist() {
// Variables for maintaining the binary heap.
Node *child_node, *child_node_1, *child_node_2;
int child_idx, child_idx_1, child_idx_2;
// Remove the Node at index 1 from the open list binary heap.
// Note: Node at index 0 of open list is a dummy node.
_open_list.erase(_open_list.begin() + 1);
if(_open_list.size() > 1) {
// Store the last element in the open list to a temp_node.
Node *temp_node = _open_list[_open_list.size() - 1];
// Shift the elements of the open list to the right by 1 element circularly, excluding element at 0 index.
for(int i = _open_list.size() - 1; i > 1; --i) {
_open_list[i] = _open_list[i - 1];
}
// Assign the temp_node to 1st element in the open list.
_open_list[1] = temp_node;
// Set the iterator for traversing the node from index 1 in the heap.
unsigned int k = 1;
// This loop traverses down the open list till the node reaches the correct position in the binary heap.
while(true) {
if((k * 2 + 1) < _open_list.size()) {
// Two children exists for the parent node.
child_idx_1 = k * 2;
child_idx_2 = k * 2 + 1;
child_node_1 = _open_list[child_idx_1];
child_node_2 = _open_list[child_idx_2];
if(_open_list[child_idx_1]->_score < _open_list[child_idx_2]->_score) {
if(_open_list[k]->_score > _open_list[child_idx_1]->_score) {
// Swap the parent node and the child node.
_open_list[child_idx_1] = _open_list[k];
_open_list[k] = child_node_1;
// Update the parent node index.
k = child_idx_1;
}
else {
break;
}
}
else {
if(_open_list[k]->_score > _open_list[child_idx_2]->_score) {
// Swap the parent node and the child node.
_open_list[child_idx_2] = _open_list[k];
_open_list[k] = child_node_2;
// Update the parent node index.
k = child_idx_2;
}
else {
break;
}
}
}
else if((k * 2) < _open_list.size()) {
// Only one child exists for the parent node.
child_idx = k * 2;
child_node = _open_list[child_idx];
if(_open_list[k]->_score > _open_list[child_idx]->_score) {
// Swap the parent node and the child node.
_open_list[child_idx] = _open_list[k];
_open_list[k] = child_node;
// Update the parent node index.
k = child_idx;
}
else {
break;
}
}
else {
// No children exists.
break;
}
}
}
// At this point the Node was succesfully removed and the binary heap re-arranged.
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : add_to_clist
// Description : This function adds a node to the closed list.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::add_to_clist(Node *nd) {
// Set the status as closed.
nd->_status = nd->close;
// Add the node to the close list.
_closed_list.push_back(nd);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : remove_from_clist
// Description : This function removes a node from the closed list.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFinder::remove_from_clist(int r, int c) {
for(unsigned int i = 0; i < _closed_list.size(); ++i) {
if(_closed_list[i]->_grid_x == r && _closed_list[i]->_grid_y == c) {
_closed_list.erase(_closed_list.begin() + i);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : find_in_mesh
// Description : This function allows the user to pass a position and it returns the
// corresponding node on the navigation mesh. A very useful function as
// it allows for dynamic updation of the mesh based on position.
/////////////////////////////////////////////////////////////////////////////////////////
Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size) {
int size = grid_size;
float x = pos[0];
float y = pos[1];
for(int i = 0; i < size; ++i) {
for(int j = 0; j < size; ++j) {
if(nav_mesh[i][j] != NULL && nav_mesh[i][j]->contains(x, y)) {
return(nav_mesh[i][j]);
}
}
}
return NULL;
}

View file

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiPathFinder.h
// Created by : Deepak, John, Navin
// Date : 10 Nov 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _PATHFINDER_H
#define _PATHFINDER_H
#include "meshNode.h"
#include "cmath.h"
#include "lineSegs.h"
typedef vector<Node *> NodeArray;
typedef vector<NodeArray> NavMesh;
Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Class : PathFinder
// Description : This class implements pathfinding using A* algorithm. It also uses a Binary Heap search to
// search the open list. The heuristics are calculated using the manhattan method.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI PathFinder {
public:
Node *_src_node;
Node *_dest_node;
vector<Node*> _open_list;
vector<Node*> _closed_list;
NavMesh _grid;
void identify_neighbors(Node *nd);
int calc_cost_frm_src(Node *nd);
int calc_heuristic(Node *nd);
void calc_node_score(Node *nd);
bool is_diagonal_node(Node *nd);
void add_to_olist(Node *nd);
void remove_from_olist();
void add_to_clist(Node *nd);
void remove_from_clist(int r, int c);
void generate_path();
void find_path(Node *src_node, Node *dest_node);
PathFinder(NavMesh nav_mesh);
~PathFinder();
};
#endif

View file

@ -0,0 +1,280 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiWorld.cxx
// Created by : Deepak, John, Navin
// Date : 8 Sep 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "aiWorld.h"
AIWorld::AIWorld(NodePath render) {
_ai_char_pool = new AICharPool();
_render = render;
}
AIWorld::~AIWorld() {
}
void AIWorld::add_ai_char(AICharacter *ai_char) {
_ai_char_pool->append(ai_char);
ai_char->_window_render = _render;
ai_char->_world = this;
}
void AIWorld::remove_ai_char(string name) {
_ai_char_pool->del(name);
remove_ai_char_from_flock(name);
}
void AIWorld::remove_ai_char_from_flock(string name) {
AICharPool::node *ai_pool;
ai_pool = _ai_char_pool->_head;
while((ai_pool) != NULL) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) {
if(ai_pool->_ai_char->_ai_char_flock_id == _flock_pool[i]->get_id()) {
for(unsigned int j = 0; j<_flock_pool[i]->_ai_char_list.size(); ++j) {
if(_flock_pool[i]->_ai_char_list[j]->_name == name) {
_flock_pool[i]->_ai_char_list.erase(_flock_pool[i]->_ai_char_list.begin() + j);
return;
}
}
}
}
ai_pool = ai_pool->_next;
}
}
void AIWorld::print_list() {
_ai_char_pool->print_list();
}
////////////////////////////////////////////////////////////////////////
// Function : update
// Description : The AIWorld update function calls the update function of all the
// AI characters which have been added to the AIWorld.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::update() {
AICharPool::node *ai_pool;
ai_pool = _ai_char_pool->_head;
while((ai_pool)!=NULL) {
ai_pool->_ai_char->update();
ai_pool = ai_pool->_next;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : add_flock
// Description : This function adds all the AI characters in the Flock object to
// the AICharPool. This function allows adding the AI characetrs as
// part of a flock.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::add_flock(Flock *flock) {
// Add all the ai_characters in the flock to the AIWorld.
for(unsigned int i = 0; i < flock->_ai_char_list.size(); ++i) {
this->add_ai_char(flock->_ai_char_list[i]);
}
// Add the flock to the flock pool.
_flock_pool.push_back(flock);
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : get_flock
// Description : This function returns a handle to the Flock whose id is passed.
/////////////////////////////////////////////////////////////////////////////////
Flock AIWorld::get_flock(unsigned int flock_id) {
for(unsigned int i=0; i < _flock_pool.size(); ++i) {
if(_flock_pool[i]->get_id() == flock_id) {
return *_flock_pool[i];
}
}
Flock *null_flock = NULL;
return *null_flock;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : remove_flock
// Description : This function removes the flock behavior completely.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::remove_flock(unsigned int flock_id) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) {
if(_flock_pool[i]->get_id() == flock_id) {
for(unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) {
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock_activate");
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock");
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->_flock_group = NULL;
}
_flock_pool.erase(_flock_pool.begin() + i);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : flock_off
// Description : This function turns off the flock behavior temporarily. Similar to
// pausing the behavior.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::flock_off(unsigned int flock_id) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) {
if(_flock_pool[i]->get_id() == flock_id) {
for(unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) {
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock_activate");
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock");
}
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : flock_on
// Description : This function turns on the flock behavior.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::flock_on(unsigned int flock_id) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) {
if(_flock_pool[i]->get_id() == flock_id) {
for(unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) {
_flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_on("flock_activate");
}
break;
}
}
}
AICharPool::AICharPool() {
_head = NULL;
}
AICharPool::~AICharPool() {
}
void AICharPool::append(AICharacter *ai_ch) {
node *q;
node *t;
if(_head == NULL) {
q = new node();
q->_ai_char = ai_ch;
q->_next = NULL;
_head = q;
}
else {
q = _head;
while( q->_next != NULL) {
q = q->_next;
}
t = new node();
t->_ai_char = ai_ch;
t->_next = NULL;
q->_next = t;
}
}
void AICharPool::del(string name) {
node *q;
node *r;
q = _head;
if(_head==NULL) {
return;
}
// Only one node in the linked list
if(q->_next == NULL) {
if(q->_ai_char->_name == name) {
_head = NULL;
delete q;
}
return;
}
r = q;
while( q != NULL) {
if( q->_ai_char->_name == name) {
// Special case
if(q == _head) {
_head = q->_next;
delete q;
return;
}
r->_next = q->_next;
delete q;
return;
}
r = q;
q = q->_next;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : print_list
// Description : This function prints the ai characters in the AICharPool. Used for
// debugging purposes.
/////////////////////////////////////////////////////////////////////////////////
void AICharPool::print_list() {
node* q;
q = _head;
while(q != NULL) {
cout<<q->_ai_char->_name<<endl;
q = q->_next;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : add_obstacle
// Description : This function adds the nodepath as an obstacle that is needed
// by the obstacle avoidance behavior.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::add_obstacle(NodePath obstacle) {
_obstacles.push_back(obstacle);
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : remove_obstacle
// Description : This function removes the nodepath from the obstacles list that is needed
// by the obstacle avoidance behavior.
/////////////////////////////////////////////////////////////////////////////////
void AIWorld::remove_obstacle(NodePath obstacle) {
for(unsigned int i = 0; i <= _obstacles.size(); ++i) {
if(_obstacles[i] == obstacle) {
_obstacles.erase(_obstacles.begin() + i);
}
}
}

View file

@ -0,0 +1,108 @@
////////////////////////////////////////////////////////////////////////
// Filename : aiWorld.h
// Created by : Deepak, John, Navin
// Date : 8 Sep 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#pragma warning (disable:4996)
#pragma warning (disable:4005)
#pragma warning(disable:4275)
#ifndef _AIWORLD_H
#define _AIWORLD_H
#include "aiGlobals.h"
#include "aiCharacter.h"
#include "flock.h"
class AICharacter;
class Flock;
///////////////////////////////////////////////////////////////////////
//
// Class : AICharPool
// Description : This class implements a linked list of AI Characters allowing
// the user to add and delete characters from the linked list.
// This will be used in the AIWorld class.
////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI AICharPool {
public:
struct node {
AICharacter * _ai_char;
node * _next;
} ;
node* _head;
AICharPool();
~AICharPool();
void append(AICharacter *ai_ch);
void del(string name);
void print_list();
};
///////////////////////////////////////////////////////////////////////
//
// Class : AIWorld
// Description : A class that implements the virtual AI world which keeps track
// of the AI characters active at any given time. It contains a linked
// list of AI characters, obstactle data and unique name for each
// character. It also updates each characters state. The AI characters
// can also be added to the world as flocks.
////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI AIWorld {
private:
AICharPool * _ai_char_pool;
NodePath _render;
public:
vector<NodePath> _obstacles;
typedef std::vector<Flock*> FlockPool;
FlockPool _flock_pool;
void remove_ai_char_from_flock(string name);
PUBLISHED:
AIWorld(NodePath render);
~AIWorld();
void add_ai_char(AICharacter *ai_ch);
void remove_ai_char(string name);
void add_flock(Flock *flock);
void flock_off(unsigned int flock_id);
void flock_on(unsigned int flock_id);
void remove_flock(unsigned int flock_id);
Flock get_flock(unsigned int flock_id);
void add_obstacle(NodePath obstacle);
void remove_obstacle(NodePath obstacle);
void print_list();
void update();
};
#endif

View file

@ -0,0 +1,122 @@
////////////////////////////////////////////////////////////////////////
// Filename : arrival.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "arrival.h"
Arrival::Arrival(AICharacter *ai_ch, double distance) {
_ai_char = ai_ch;
_arrival_distance = distance;
_arrival_done = false;
}
Arrival::~Arrival() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_arrival
// Description : This function performs the arrival and returns an arrival force which is used
// in the calculate_prioritized function.
// In case the steering force = 0, it resets to arrival_activate.
// The arrival behavior works only when seek or pursue is active.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Arrival::do_arrival() {
LVecBase3 direction_to_target;
double distance;
if(_arrival_type) {
direction_to_target = _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render) - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
}
else {
direction_to_target = _ai_char->get_ai_behaviors()->_seek_obj->_seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
}
distance = direction_to_target.length();
_arrival_direction = direction_to_target;
_arrival_direction.normalize();
if(int(distance) == 0) {
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
_ai_char->_steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0);
if(_ai_char->_steering->_seek_obj != NULL) {
_ai_char->_steering->turn_off("arrival");
_ai_char->_steering->turn_on("arrival_activate");
}
_arrival_done = true;
return(LVecBase3(0.0, 0.0, 0.0));
}
else {
_arrival_done = false;
}
double u = _ai_char->get_velocity().length();
LVecBase3 desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass();
if(_ai_char->_steering->_seek_obj != NULL) {
return(desired_force);
}
if(_ai_char->_steering->_pursue_obj != NULL) {
if(distance > _arrival_distance) {
_ai_char->_steering->turn_off("arrival");
_ai_char->_steering->turn_on("arrival_activate");
_ai_char->_steering->resume_ai("pursue");
}
return(desired_force);
}
cout<<"Arrival works only with seek and pursue"<<endl;
return(LVecBase3(0.0, 0.0, 0.0));
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : arrival_activate
// Description : This function checks for whether the target is within the arrival distance.
// When this is true, it calls the do_arrival function and sets the arrival direction.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
void Arrival::arrival_activate() {
LVecBase3 dirn;
if(_arrival_type) {
dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render));
}
else {
dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_seek_obj->_seek_position);
}
double distance = dirn.length();
if(distance < _arrival_distance && _ai_char->_steering->_steering_force.length() > 0) {
_ai_char->_steering->turn_off("arrival_activate");
_ai_char->_steering->turn_on("arrival");
if(_ai_char->_steering->is_on(_ai_char->_steering->_seek)) {
_ai_char->_steering->turn_off("seek");
}
if(_ai_char->_steering->is_on(_ai_char->_steering->_pursue)) {
_ai_char->_steering->pause_ai("pursue");
}
}
}

View file

@ -0,0 +1,46 @@
////////////////////////////////////////////////////////////////////////
// Filename : arrival.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _ARRIVAL_H
#define _ARRIVAL_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Arrival {
public:
AICharacter *_ai_char;
NodePath _arrival_target;
LVecBase3 _arrival_target_pos;
double _arrival_distance;
LVecBase3 _arrival_direction;
bool _arrival_done;
// This flag specifies if the arrival behavior is being used with seek or pursue behavior.
// True = used with pursue.
// False = used with seek.
bool _arrival_type;
Arrival(AICharacter *ai_ch, double distance = 10.0);
~Arrival();
LVecBase3 do_arrival();
void arrival_activate();
};
#endif

View file

@ -0,0 +1,55 @@
// Filename: config_ai.cxx
// Created by: Pandai (13Sep09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "config_ai.h"
#include "aiWorld.h"
#include "aiCharacter.h"
#include "aiBehaviors.h"
#include "seek.h"
#include "flee.h"
#include "pursue.h"
#include "evade.h"
#include "arrival.h"
#include "flock.h"
#include "wander.h"
#include "pathFollow.h"
#include "obstacleAvoidance.h"
#include "pathFind.h"
#include "aiNode.h"
#include "aiPathFinder.h"
#include "dconfig.h"
Configure(config_ai);
NotifyCategoryDef(ai, "");
ConfigureFn(config_ai) {
init_libai();
}
////////////////////////////////////////////////////////////////////
// Function: init_libai
// Description: Initializes the library. This must be called at
// least once before any of the functions or classes in
// this library can be used. Normally it will be
// called by the static initializers and need not be
// called explicitly, but special cases exist.
////////////////////////////////////////////////////////////////////
void
init_libai() {
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
}

View file

@ -0,0 +1,26 @@
// Filename: config_ai.h
// Created by: Pandai (13Sep09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef CONFIG_AI_H
#define CONFIG_AI_H
#include "contribbase.h"
#include "notifyCategoryProxy.h"
NotifyCategoryDecl(ai, EXPCL_PANDAAI, EXPTP_PANDAAI);
extern EXPCL_PANDAAI void init_libai();
#endif

View file

@ -0,0 +1,88 @@
////////////////////////////////////////////////////////////////////////
// Filename : evade.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "evade.h"
Evade::Evade(AICharacter *ai_ch, NodePath target_object, double panic_distance,
double relax_distance, float evade_wt) {
_ai_char = ai_ch;
_evade_target = target_object;
_evade_distance = panic_distance;
_evade_relax_distance = relax_distance;
_evade_weight = evade_wt;
_evade_done = true;
_evade_activate_done = false;
}
Evade::~Evade() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_evade
// Description : This function performs the evade and returns an evade force which is used
// in the calculate_prioritized function.
// In case the AICharacter is past the (panic + relax) distance,
// it resets to evade_activate.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Evade::do_evade() {
assert(_evade_target && "evade target not assigned");
_evade_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _evade_target.get_pos(_ai_char->_window_render);
double distance = _evade_direction.length();
_evade_direction.normalize();
LVecBase3 desired_force = _evade_direction * _ai_char->_movt_force;
if(distance > (_evade_distance + _evade_relax_distance)) {
if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_evade) == _ai_char->_steering->_evade) {
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
}
_ai_char->_steering->turn_off("evade");
_ai_char->_steering->turn_on("evade_activate");
_evade_done = true;
return(LVecBase3(0.0, 0.0, 0.0));
}
else {
_evade_done = false;
return(desired_force);
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : evade_activate
// Description : This function checks for whether the target is within the panic distance.
// When this is true, it calls the do_evade function and sets the evade direction.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
void Evade::evade_activate() {
_evade_direction = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _evade_target.get_pos(_ai_char->_window_render));
double distance = _evade_direction.length();
_evade_activate_done = false;
if(distance < _evade_distance) {
_ai_char->_steering->turn_off("evade_activate");
_ai_char->_steering->turn_on("evade");
_evade_activate_done = true;
}
}

View file

@ -0,0 +1,45 @@
////////////////////////////////////////////////////////////////////////
// Filename : evade.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _EVADE_H
#define _EVADE_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Evade {
public:
AICharacter *_ai_char;
NodePath _evade_target;
float _evade_weight;
LVecBase3 _evade_direction;
double _evade_distance;
double _evade_relax_distance;
bool _evade_done;
bool _evade_activate_done;
Evade(AICharacter *ai_ch, NodePath target_object, double panic_distance,
double relax_distance, float evade_wt);
~Evade();
LVecBase3 do_evade();
void evade_activate();
};
#endif

View file

@ -0,0 +1,109 @@
////////////////////////////////////////////////////////////////////////
// Filename : flee.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "flee.h"
Flee::Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance,
double relax_distance, float flee_wt){
_ai_char = ai_ch;
_flee_position = target_object.get_pos(_ai_char->_window_render);
_flee_distance = panic_distance;
_flee_weight = flee_wt;
_flee_relax_distance = relax_distance;
_flee_done = false;
_flee_activate_done = false;
}
Flee::Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance,
double relax_distance, float flee_wt){
_ai_char = ai_ch;
_flee_position = pos;
_flee_distance = panic_distance;
_flee_weight = flee_wt;
_flee_relax_distance = relax_distance;
_flee_done = false;
_flee_activate_done = false;
}
Flee::~Flee() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_flee
// Description : This function performs the flee and returns a flee force which is used
// in the calculate_prioritized function.
// In case the AICharacter is past the (panic + relax) distance,
// it resets to flee_activate.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Flee::do_flee() {
LVecBase3 dirn;
double distance;
LVecBase3 desired_force;
dirn = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_present_pos;
distance = dirn.length();
desired_force = _flee_direction * _ai_char->_movt_force;
if(distance > (_flee_distance + _flee_relax_distance)) {
if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_flee) == _ai_char->_steering->_flee) {
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
}
_flee_done = true;
_ai_char->_steering->turn_off("flee");
_ai_char->_steering->turn_on("flee_activate");
return(LVecBase3(0.0, 0.0, 0.0));
}
else {
return(desired_force);
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : flee_activate
// Description : This function checks for whether the target is within the panic distance.
// When this is true, it calls the do_flee function and sets the flee direction.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
void Flee::flee_activate() {
LVecBase3 dirn;
double distance;
_flee_activate_done = false;
dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_position);
distance = dirn.length();
if(distance < _flee_distance) {
_flee_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_position;
_flee_direction.normalize();
_flee_present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
_ai_char->_steering->turn_off("flee_activate");
_ai_char->_steering->turn_on("flee");
_flee_activate_done = true;
}
}

View file

@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////
// Filename : flee.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _FLEE_H
#define _FLEE_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Flee {
public:
AICharacter *_ai_char;
LVecBase3 _flee_position;
float _flee_weight;
LVecBase3 _flee_direction;
double _flee_distance;
double _flee_relax_distance;
LVecBase3 _flee_present_pos;
bool _flee_done;
bool _flee_activate_done;
Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance = 10.0,
double relax_distance = 10.0, float flee_wt = 1.0);
Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance = 10.0,
double relax_distance = 10.0, float flee_wt = 1.0);
~Flee();
LVecBase3 do_flee();
void flee_activate();
};
#endif

View file

@ -0,0 +1,46 @@
////////////////////////////////////////////////////////////////////////
// Filename : flock.cxx
// Created by : Deepak, John, Navin
// Date : 12 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "flock.h"
Flock::Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt,
unsigned int cohesion_wt, unsigned int alignment_wt) {
_flock_id = flock_id;
_flock_vcone_angle = vcone_angle;
_flock_vcone_radius = vcone_radius;
_separation_wt = separation_wt;
_cohesion_wt = cohesion_wt;
_alignment_wt = alignment_wt;
}
Flock::~Flock() {
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : add_ai_char
// Description : This function adds AI characters to the flock.
/////////////////////////////////////////////////////////////////////////////////////////
void Flock::add_ai_char(AICharacter *ai_char) {
ai_char->_ai_char_flock_id = _flock_id;
ai_char->_steering->_flock_group = this;
_ai_char_list.push_back(ai_char);
}
unsigned int Flock::get_id() {
return _flock_id;
}

View file

@ -0,0 +1,63 @@
////////////////////////////////////////////////////////////////////////
// Filename : flock.h
// Created by : Deepak, John, Navin
// Date : 12 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _FLOCK_H
#define _FLOCK_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
///////////////////////////////////////////////////////////////////////////////////////
//
// Class : Flock
// Description : This class is used to define the flock attributes and the AI characters
// which are part of the flock.
///////////////////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI Flock {
private:
unsigned int _flock_id;
public:
// Variables which will hold the parameters of the ai character's visibilty cone.
double _flock_vcone_angle;
double _flock_vcone_radius;
// Variables to specify weights of separation, cohesion and alignment behaviors and thus
// create variable flock behavior.
unsigned int _separation_wt;
unsigned int _cohesion_wt;
unsigned int _alignment_wt;
// This vector will hold all the ai characters which belong to this flock.
typedef std::vector<AICharacter*> AICharList;
AICharList _ai_char_list;
PUBLISHED:
Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2,
unsigned int cohesion_wt = 4, unsigned int alignment_wt = 1);
~Flock();
// Function to add the ai characters to _ai_char_list.
void add_ai_char(AICharacter *ai_char);
// Function to access the private member flock_id.
unsigned int get_id();
};
#endif

View file

@ -0,0 +1,43 @@
#include "meshNode.h"
Node::Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) {
for(int i = 0; i < 8; ++i) {
_neighbours[i] = NULL;
}
_position = pos;
_width = w;
_length = l;
_height = h;
_grid_x = grid_x;
_grid_y = grid_y;
_status = neutral;
_type = true;
_score = 0;
_cost = 0;
_heuristic = 0;
_next = NULL;
_prv_node = NULL;
}
Node::~Node() {
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : contains
// Description : This is a handy function which returns true if the passed position is
// within the node's dimensions.
/////////////////////////////////////////////////////////////////////////////////////////
bool Node::contains(float x, float y) {
if(_position.get_x() - _width / 2 <= x && _position.get_x() + _width / 2 >= x &&
_position.get_y() - _length / 2 <= y && _position.get_y() + _length / 2 >= y) {
return true;
}
else {
return false;
}
}

View file

@ -0,0 +1,67 @@
#ifndef _MESHNODE_H
#define _MESHNODE_H
#include "aiGlobals.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Class : Node
// Description : This class is used to assign the nodes on the mesh. It holds all the data necessary to
// compute A* algorithm. It also maintains a lot of vital information such as the neighbor
// nodes of each node and also its position on the mesh.
// Note: The Mesh Generator which is a stand alone tool makes use of this class to generate the nodes on the
// mesh.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI Node {
public:
// This variable specifies whether the node is an obtacle or not.
// Used for dynamic obstacle addition to the environment.
// obstacle = false
// navigational = true
bool _type;
// This variable specifies the node status whether open, close or neutral.
// open = belongs to _open_list.
// close = belongs to _closed_list.
// neutral = unexamined node.
enum Status {
open,
close,
neutral
};
Status _status;
// The score is used to compute the traversal expense to nodes when using A*.
// _score = _cost + heuristic
int _score;
int _cost;
int _heuristic;
// Used to trace back the path after it is generated using A*.
Node *_prv_node;
// Position of the node in the 2d grid.
int _grid_x, _grid_y;
// Position of the node in 3D space.
LVecBase3 _position;
// Dimensions of each face / cell on the mesh.
// Height is given in case of expansion to a 3d mesh. Currently not used.
float _width, _length ,_height;
Node *_neighbours[8]; // anti-clockwise from top left corner.
// The _next pointer is used for traversal during mesh generation from the model.
// Note: The data in this member is discarded when mesh data is written into navmesh.csv file.
Node *_next;
Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
~Node();
bool contains(float x, float y);
};
#endif

View file

@ -0,0 +1,116 @@
////////////////////////////////////////////////////////////////////////
// Filename : obstacleAvoidance.cxx
// Created by : Deepak, John, Navin
// Date : 10 Nov 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "obstacleAvoidance.h"
ObstacleAvoidance::ObstacleAvoidance(AICharacter *ai_char, float feeler_length) {
_ai_char = ai_char;
_feeler = feeler_length;
}
ObstacleAvoidance::~ObstacleAvoidance() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : obstacle_detection
// Description : This function checks if an obstacle is near to the AICharacter and
// if an obstacle is detected returns true
/////////////////////////////////////////////////////////////////////////////////
bool ObstacleAvoidance::obstacle_detection() {
// Calculate the volume of the AICharacter with respect to render
PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
LVecBase3 avoidance(0.0, 0.0, 0.0);
double distance = 0x7fff ;
double expanded_radius;
LVecBase3 to_obstacle;
LVecBase3 prev_avoidance;
for(unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) {
PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds();
CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
LVecBase3 near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos();
// Check if it's the nearest obstacle, If so initialize as the nearest obstacle
if((near_obstacle.length() < distance) && (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) {
_nearest_obstacle = _ai_char->_world->_obstacles[i];
distance = near_obstacle.length();
expanded_radius = bsphere->get_radius() + np_sphere->get_radius();
}
}
LVecBase3 feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
feeler.normalize();
feeler *= (expanded_radius + np_sphere->get_radius()) ;
to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos();
LVector3 line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
LVecBase3 project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared();
LVecBase3 perp = project - to_obstacle;
// If the nearest obstacle will collide with our AICharacter then send obstacle detection as true
if((_nearest_obstacle) && (perp.length() < expanded_radius - np_sphere->get_radius()) && (project.length() < feeler.length())) {
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : obstacle_avoidance_activate
// Description : This function activates obstacle_avoidance if a obstacle
// is detected
/////////////////////////////////////////////////////////////////////////////////
void ObstacleAvoidance::obstacle_avoidance_activate() {
if(obstacle_detection()) {
_ai_char->_steering->turn_off("obstacle_avoidance_activate");
_ai_char->_steering->turn_on("obstacle_avoidance");
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_obstacle_avoidance
// Description : This function returns the force necessary by the AICharacter to
// avoid the nearest obstacle detected by obstacle_detection
// function
// NOTE : This assumes the obstacles are spherical
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 ObstacleAvoidance::do_obstacle_avoidance() {
LVecBase3 offset = _ai_char->get_node_path().get_pos() - _nearest_obstacle.get_pos();
PT(BoundingVolume) bounds =_nearest_obstacle.get_bounds();
CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
double distance_needed = offset.length() - bsphere->get_radius() - np_sphere->get_radius();
if((obstacle_detection())) {
LVecBase3 direction = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
direction.normalize();
float forward_component = offset.dot(direction);
LVecBase3 projection = forward_component * direction;
LVecBase3 perpendicular_component = offset - projection;
double p = perpendicular_component.length();
perpendicular_component.normalize();
LVecBase3 avoidance = perpendicular_component;
// The more closer the obstacle, the more force it generates
avoidance = (avoidance * _ai_char->get_max_force() * _ai_char->_movt_force) / (p + 0.01);
return avoidance;
}
_ai_char->_steering->turn_on("obstacle_avoidance_activate");
_ai_char->_steering->turn_off("obstacle_avoidance");
return LVecBase3(0, 0, 0);
}

View file

@ -0,0 +1,39 @@
#ifndef OBSTACLE_AVOIDANCE_H
#define OBSTACLE_AVOIDANCE_H
////////////////////////////////////////////////////////////////////////
// Filename : obstacleAvoidance.h
// Created by : Deepak, John, Navin
// Date : 10 Nov 2009
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "aiCharacter.h"
#include "boundingSphere.h"
class AICharacter;
class EXPCL_PANDAAI ObstacleAvoidance {
public :
AICharacter *_ai_char;
float _obstacle_avoidance_weight;
NodePath _nearest_obstacle;
bool _obstacle_avoidance_done;
float _feeler;
ObstacleAvoidance(AICharacter *ai_char, float feeler_length);
LVecBase3 do_obstacle_avoidance();
~ObstacleAvoidance();
void obstacle_avoidance_activate();
bool obstacle_detection();
};
#endif

View file

@ -0,0 +1 @@
#include "ai_composite1.cxx"

View file

@ -0,0 +1,21 @@
#include "config_ai.cxx"
#include "aiWorld.cxx"
#include "aiCharacter.cxx"
#include "aiBehaviors.cxx"
#include "seek.cxx"
#include "flee.cxx"
#include "pursue.cxx"
#include "evade.cxx"
#include "arrival.cxx"
#include "flock.cxx"
#include "wander.cxx"
#include "pathFollow.cxx"
#include "obstacleAvoidance.cxx"
#include "pathFind.cxx"
#include "aiNode.cxx"
#include "aiPathFinder.cxx"
#include "meshNode.cxx"

View file

@ -0,0 +1,417 @@
#include "pathFind.h"
PathFind::PathFind(AICharacter *ai_ch) {
_ai_char = ai_ch;
_parent = new GeomNode("parent");
_ai_char->_window_render.attach_new_node(_parent);
_pen = new LineSegs("pen");
_pen->set_color(1.0, 0.0, 0.0);
_pen->set_thickness(2.0);
_path_finder_obj = NULL;
_dynamic_avoid = false;
}
PathFind::~PathFind() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : create_nav_mesh
// Description : This function recreates the navigation mesh from the .csv file
/////////////////////////////////////////////////////////////////////////////////
void PathFind::create_nav_mesh(const char* navmesh_filename) {
// Stage variables.
int grid_x, grid_y;
float l, w, h;
LVecBase3 position;
// Variable to hold line data read from file.
string line;
// Array for storing data members obtained from each line of the file.
string fields[10];
// Open data file for reading.
ifstream nav_mesh_file (navmesh_filename);
if(nav_mesh_file.is_open()) {
// Capture the grid size from the file.
getline(nav_mesh_file, line);
int pos = line.find(",");
_grid_size = atoi((line.substr(pos + 1)).c_str());
// Initialize the stage mesh with NULL nodes.
for(int r = 0; r < _grid_size; ++r) {
_nav_mesh.push_back(vector<Node*>());
for(int c = 0; c < _grid_size; ++c) {
_nav_mesh[r].push_back(NULL);
}
}
// Ignore the header of the navmesh.csv file.
getline(nav_mesh_file, line);
// Begin reading data from the file.
while(!nav_mesh_file.eof()) {
getline(nav_mesh_file, line);
stringstream linestream (line);
// Stores all the data members in the line to the array.
// Data structure: NULL,NodeType,GridX,GridY,Length,Width,Height,PosX,PosY,PosZ
for(int i = 0; i < 10; ++i) {
getline(linestream, fields[i], ',');
}
// Populate the main nodes into stage mesh.
if(fields[0] == "0" && fields[1] == "0") {
grid_x = atoi(fields[2].c_str());
grid_y = atoi(fields[3].c_str());
l = atof(fields[4].c_str());
w = atof(fields[5].c_str());
h = atof(fields[6].c_str());
position = LVecBase3(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str()));
Node *stage_node = new Node(grid_x, grid_y, position, w, l, h);
_nav_mesh[grid_y][grid_x] = stage_node;
}
else if(fields[0] == "") {
// End of file reached at this point.
nav_mesh_file.close();
// Assign the neighbor nodes for each of the main nodes that just got populated into the stage mesh.
assign_neighbor_nodes(navmesh_filename);
}
}
}
else {
cout<<"error opening navmesh.csv file!"<<endl;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : assign_neighbor_nodes
// Description : This function assigns the neighbor nodes for each main node present in
// _nav_mesh.
/////////////////////////////////////////////////////////////////////////////////
void PathFind::assign_neighbor_nodes(const char* navmesh_filename){
ifstream nav_mesh_file (navmesh_filename);
// Stage variables.
int gd_x, gd_y, gd_xn, gd_yn;
string ln;
string fields[10];
string fields_n[10];
if(nav_mesh_file.is_open()) {
getline(nav_mesh_file, ln); // Get rid of grid size line.
getline(nav_mesh_file, ln); // Get rid of the header.
while(!nav_mesh_file.eof()) {
getline(nav_mesh_file, ln); // Gets main node data only. No neighbor nodes.
stringstream linestream (ln);
for(int i = 0; i < 10; ++i) {
getline(linestream, fields[i], ',');
}
if(fields[0] == "0" && fields[1] == "0") {
// Usable main node.
gd_x = atoi(fields[2].c_str());
gd_y = atoi(fields[3].c_str());
for(int i = 0; i < 8; ++i) {
getline(nav_mesh_file, ln); // Gets neighbor node data only. No main nodes.
stringstream linestream_n (ln);
for(int j = 0; j < 10; ++j) {
getline(linestream_n, fields_n[j], ',');
}
gd_xn = atoi(fields_n[2].c_str());
gd_yn = atoi(fields_n[3].c_str());
if(fields_n[0] == "0" && fields_n[1] == "1") {
// Usable neighbor for main node.
// TODO: The indices of the vector are inverted when compared to the values of the nodes on actual grid. Fix this!
_nav_mesh[gd_y][gd_x]->_neighbours[i] = _nav_mesh[gd_yn][gd_xn];
}
else if(fields_n[0] == "1" && fields_n[1] == "1") {
// NULL neighbor.
_nav_mesh[gd_y][gd_x]->_neighbours[i] = NULL;
}
else {
cout<<"Warning: Corrupt data!"<<endl;
}
}
}
else if(fields[0] == "") {
// End of file reached at this point.
nav_mesh_file.close();
}
}
}
else {
cout<<"error opening navmesh.csv file!"<<endl;
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : set_path_find
// Description : This function starts the path finding process after reading the given
// navigation mesh.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::set_path_find(const char* navmesh_filename) {
create_nav_mesh(navmesh_filename);
if(_ai_char->_steering->_path_follow_obj) {
_ai_char->_steering->remove_ai("pathfollow");
}
_ai_char->_steering->path_follow(1.0f);
if(_path_finder_obj) {
delete _path_finder_obj;
_path_finder_obj = NULL;
}
_path_finder_obj = new PathFinder(_nav_mesh);
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : path_find (for pathfinding towards a static position)
// Description : This function checks for the source and target in the navigation mesh
// for its availability and then finds the best path via the A* algorithm
// Then it calls the path follower to make the object follow the path.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::path_find(LVecBase3 pos, string type) {
if(type == "addPath") {
if(_ai_char->_steering->_path_follow_obj) {
_ai_char->_steering->remove_ai("pathfollow");
}
_ai_char->_steering->path_follow(1.0f);
}
clear_path();
Node* src = find_in_mesh(_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size);
if(src == NULL) {
cout<<"couldnt find source"<<endl;
}
Node* dst = find_in_mesh(_nav_mesh, pos, _grid_size);
if(dst == NULL) {
cout<<"couldnt find destination"<<endl;
}
if(src != NULL && dst != NULL) {
_path_finder_obj->find_path(src, dst);
trace_path(src);
}
if(!_ai_char->_steering->_path_follow_obj->_start) {
_ai_char->_steering->start_follow();
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : path_find (for pathfinding towards a moving target (a NodePath))
// Description : This function checks for the source and target in the navigation mesh
// for its availability and then finds the best path via the A* algorithm
// Then it calls the path follower to make the object follow the path.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::path_find(NodePath target, string type) {
if(type == "addPath") {
if(_ai_char->_steering->_path_follow_obj) {
_ai_char->_steering->remove_ai("pathfollow");
}
_ai_char->_steering->path_follow(1.0f);
}
clear_path();
_path_find_target = target;
_prev_position = target.get_pos(_ai_char->_window_render);
Node* src = find_in_mesh(_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size);
if(src == NULL) {
cout<<"couldnt find source"<<endl;
}
Node* dst = find_in_mesh(_nav_mesh, _prev_position, _grid_size);
if(dst == NULL) {
cout<<"couldnt find destination"<<endl;
}
if(src != NULL && dst != NULL) {
_path_finder_obj->find_path(src, dst);
trace_path(src);
}
if(_ai_char->_steering->_path_follow_obj!=NULL) {
if(!_ai_char->_steering->_path_follow_obj->_start) {
_ai_char->_steering->start_follow("pathfind");
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : clear_path
// Description : Helper function to restore the path and mesh to its initial state
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::clear_path() {
// Initialize to zero
for(int i = 0; i < _grid_size; ++i) {
for(int j = 0; j < _grid_size; ++j) {
if(_nav_mesh[i][j] != NULL) {
_nav_mesh[i][j]->_status = _nav_mesh[i][j]->neutral;
_nav_mesh[i][j]->_cost = 0;
_nav_mesh[i][j]->_heuristic = 0;
_nav_mesh[i][j]->_score = 0;
_nav_mesh[i][j]->_prv_node = NULL;
}
}
}
if(_path_finder_obj) {
_path_finder_obj->_open_list.clear();
_path_finder_obj->_closed_list.clear();
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : trace_path
// Description : This function is the function which sends the path information one by
// one to the path follower so that it can store the path needed to be
// traversed by the pathfinding object
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::trace_path(Node* src) {
if(_ai_char->_pf_guide) {
_parent->remove_all_children();
}
else {
_parent->remove_all_children();
}
if(_path_finder_obj->_closed_list.size() > 0) {
Node *traversor = _path_finder_obj->_closed_list[_path_finder_obj->_closed_list.size() - 0.5];
while(traversor != src) {
if(_ai_char->_pf_guide) {
_pen->move_to(traversor->_position.get_x(), traversor->_position.get_y(), 1);
_pen->draw_to(traversor->_prv_node->_position.get_x(), traversor->_prv_node->_position.get_y(), 0.5);
PT(GeomNode) gnode = _pen->create();
_parent->add_child(gnode);
}
_ai_char->_steering->add_to_path(traversor->_position);
traversor = traversor->_prv_node;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : add_obstacle_to_mesh
// Description : This function allows the user to dynamically add obstacles to the
// game environment. The function will update the nodes within the
// bounding volume of the obstacle as non-traversable. Hence will not be
// considered by the pathfinding algorithm.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::add_obstacle_to_mesh(NodePath obstacle) {
PT(BoundingVolume) np_bounds = obstacle.get_bounds();
CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
Node* temp = find_in_mesh(_nav_mesh, obstacle.get_pos(), _grid_size);
if(temp != NULL) {
float left = temp->_position.get_x() - np_sphere->get_radius();
float right = temp->_position.get_x() + np_sphere->get_radius();
float top = temp->_position.get_y() + np_sphere->get_radius();
float down = temp->_position.get_y() - np_sphere->get_radius();
for(int i = 0; i < _grid_size; ++i) {
for(int j = 0; j < _grid_size; ++j) {
if(_nav_mesh[i][j] != NULL && _nav_mesh[i][j]->_type == true) {
if(_nav_mesh[i][j]->_position.get_x() >= left && _nav_mesh[i][j]->_position.get_x() <= right &&
_nav_mesh[i][j]->_position.get_y() >= down && _nav_mesh[i][j]->_position.get_y() <= top) {
_nav_mesh[i][j]->_type = false;
_previous_obstacles.insert(_previous_obstacles.end(), i);
_previous_obstacles.insert(_previous_obstacles.end(), j);
}
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : do_dynamic_avoid()
// Description : This function does the updation of the collisions to the mesh based
// on the new positions of the obstacles.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::do_dynamic_avoid() {
clear_previous_obstacles();
_previous_obstacles.clear();
for(unsigned int i = 0; i < _dynamic_obstacle.size(); ++i) {
add_obstacle_to_mesh(_dynamic_obstacle[i]);
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : clear_previous_obstacles()
// Description : Helper function to reset the collisions if the obstacle is not on the
// node anymore
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::clear_previous_obstacles(){
for(unsigned int i = 0; i < _previous_obstacles.size(); i = i + 2) {
_nav_mesh[_previous_obstacles[i]][_previous_obstacles[i + 1]]->_type = true;
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Function : dynamic_avoid
// Description : This function starts the pathfinding obstacle navigation for the
// passed in obstacle.
///////////////////////////////////////////////////////////////////////////////////////
void PathFind::dynamic_avoid(NodePath obstacle) {
_dynamic_avoid = true;
_dynamic_obstacle.insert(_dynamic_obstacle.end(), obstacle);
}

View file

@ -0,0 +1,72 @@
////////////////////////////////////////////////////////////////////////
// Filename : pathFind.h
// Created by : Deepak, John, Navin
// Date : 12 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _PATHFIND_H
#define _PATHFIND_H
#include "aiGlobals.h"
#include "aiCharacter.h"
#include "aiPathFinder.h"
#include "boundingSphere.h"
class AICharacter;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Class : PathFind
// Description : This class contains all the members and functions that are required to form an interface between
// the AIBehaviors class and the PathFinder class. An object (pointer) of this class is provided in the
// AIBehaviors class. It is only via this object that the user can activate pathfinding.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class EXPCL_PANDAAI PathFind {
public:
AICharacter *_ai_char;
PathFinder *_path_finder_obj;
NavMesh _nav_mesh;
NavMesh _stage_mesh;
int _grid_size;
NodePath _path_find_target;
LVecBase3 _prev_position;
PT(GeomNode) _parent;
LineSegs *_pen;
vector<int> _previous_obstacles;
bool _dynamic_avoid;
vector<NodePath> _dynamic_obstacle;
PathFind(AICharacter *ai_ch);
~PathFind();
void clear_path();
void trace_path(Node* src);
void create_nav_mesh(const char* navmesh_filename);
void assign_neighbor_nodes(const char* navmesh_filename);
void do_dynamic_avoid();
void clear_previous_obstacles();
void set_path_find(const char* navmesh_filename);
void path_find(LVecBase3 pos, string type = "normal");
void path_find(NodePath target, string type = "normal");
void add_obstacle_to_mesh(NodePath obstacle);
void dynamic_avoid(NodePath obstacle);
};
#endif

View file

@ -0,0 +1,131 @@
#include "pathFollow.h"
PathFollow::PathFollow(AICharacter *ai_ch, float follow_wt) {
_follow_weight = follow_wt;
_curr_path_waypoint = -1;
_start = false;
_ai_char = ai_ch;
_myClock = ClockObject::get_global_clock();
}
PathFollow::~PathFollow() {
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : add_to_path
// Description : This function adds the positions generated from a pathfind or a simple
// path follow behavior to the _path list.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFollow::add_to_path(LVecBase3 pos) {
_path.push_back(pos);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : start
// Description : This function initiates the path follow behavior.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFollow::start(string type) {
_type = type;
_start = true;
if(_path.size() > 0) {
_curr_path_waypoint = _path.size() - 1;
_dummy = _ai_char->_window_render.attach_new_node("dummy");
_dummy.set_pos(_path[_curr_path_waypoint]);
_ai_char->_steering->pursue(_dummy, _follow_weight);
_time = _myClock->get_real_time();
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : do_follow
// Description : This function allows continuous path finding by ai chars. There are 2
// ways in which this is implemented.
// 1. The character re-calculates the optimal path everytime the target
// changes its position. Less computationally expensive.
// 2. The character continuosly re-calculates its optimal path to the
// target. This is used in a scenario where the ai chars have to avoid
// other ai chars. More computationally expensive.
/////////////////////////////////////////////////////////////////////////////////////////
void PathFollow::do_follow() {
if((_myClock->get_real_time() - _time) > 0.5) {
if(_type=="pathfind") {
// This 'if' statement when 'true' causes the path to be re-calculated irrespective of target position.
// This is done when _dynamice_avoid is active. More computationally expensive.
if(_ai_char->_steering->_path_find_obj->_dynamic_avoid) {
_ai_char->_steering->_path_find_obj->do_dynamic_avoid();
if(check_if_possible()) {
_path.clear();
_ai_char->_steering->_path_find_obj->path_find(_ai_char->_steering->_path_find_obj->_path_find_target);
// Ensure that the path size is not 0.
if(_path.size() > 0) {
_curr_path_waypoint = _path.size() - 1;
_dummy.set_pos(_path[_curr_path_waypoint]);
}
else {
// Refresh the _curr_path_waypoint value if path size is <= 0.
_curr_path_waypoint = -1;
}
}
}
// This 'if' statement causes the path to be re-calculated only when there is a change in target position.
// Less computationally expensive.
else if(_ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render)
!= _ai_char->_steering->_path_find_obj->_prev_position) {
if(check_if_possible()) {
_path.clear();
_ai_char->_steering->_path_find_obj->path_find(_ai_char->_steering->_path_find_obj->_path_find_target);
// Ensure that the path size is not 0.
if(_path.size() > 0) {
_curr_path_waypoint = _path.size() - 1;
_dummy.set_pos(_path[_curr_path_waypoint]);
}
else {
// Refresh the _curr_path_waypoint value if path size is 0.
_curr_path_waypoint = -1;
}
}
}
_time = _myClock->get_real_time();
}
}
if(_curr_path_waypoint > 0) {
double distance = (_path[_curr_path_waypoint] - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length();
if(distance < 5) {
_curr_path_waypoint--;
_dummy.set_pos(_path[_curr_path_waypoint]);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function : check_if_possible
// Description : This function checks if the current positions of the ai char and the
// target char can be used to generate an optimal path.
/////////////////////////////////////////////////////////////////////////////////////////
bool PathFollow::check_if_possible() {
Node* src = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _ai_char->_steering->_path_find_obj->_grid_size);
LVecBase3 _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render);
Node* dst = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _prev_position, _ai_char->_steering->_path_find_obj->_grid_size);
if(src && dst) {
return true;
}
else {
return false;
}
}

View file

@ -0,0 +1,32 @@
#ifndef _PATHFOLLOW_H
#define _PATHFOLLOW_H
#include "aiGlobals.h"
#include "aiCharacter.h"
#include "meshNode.h"
class AICharacter;
class EXPCL_PANDAAI PathFollow {
public:
AICharacter *_ai_char;
float _follow_weight;
vector<LVecBase3> _path;
int _curr_path_waypoint;
bool _start;
NodePath _dummy;
string _type;
ClockObject *_myClock;
float _time;
PathFollow(AICharacter *ai_ch, float follow_wt);
~PathFollow();
void add_to_path(LVecBase3 pos);
void start(string type);
void do_follow();
bool check_if_possible();
};
#endif

View file

@ -0,0 +1,61 @@
////////////////////////////////////////////////////////////////////////
// Filename : pursue.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pursue.h"
Pursue::Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt) {
_ai_char = ai_ch;
_pursue_target = target_object;
_pursue_weight = pursue_wt;
_pursue_done = false;
}
Pursue::~Pursue() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_pursue
// Description : This function performs the pursue and returns a pursue force which is used
// in the calculate_prioritized function.
// In case the target has been reached it resets the forces to 0 so that the character stops.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Pursue::do_pursue() {
assert(_pursue_target && "pursue target not assigned");
LVecBase3 present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
double target_distance = (_pursue_target.get_pos(_ai_char->_window_render) - present_pos).length();
if(int(target_distance) == 0) {
_pursue_done = true;
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
_ai_char->_steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0);
return(LVecBase3(0.0, 0.0, 0.0));
}
else {
_pursue_done = false;
}
_pursue_direction = _pursue_target.get_pos(_ai_char->_window_render) - present_pos;
_pursue_direction.normalize();
LVecBase3 desired_force = _pursue_direction * _ai_char->_movt_force;
return(desired_force);
}

View file

@ -0,0 +1,39 @@
////////////////////////////////////////////////////////////////////////
// Filename : pursue.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _PURSUE_H
#define _PURSUE_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Pursue {
public:
AICharacter *_ai_char;
NodePath _pursue_target;
float _pursue_weight;
LVecBase3 _pursue_direction;
bool _pursue_done;
Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt);
~Pursue();
LVecBase3 do_pursue();
};
#endif

View file

@ -0,0 +1,66 @@
////////////////////////////////////////////////////////////////////////
// Filename : seek.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "seek.h"
Seek::Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt) {
_ai_char = ai_ch;
_seek_position = target_object.get_pos(_ai_char->_window_render);
_seek_weight = seek_wt;
_seek_direction = _seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
_seek_direction.normalize();
_seek_done = false;
}
Seek::Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt) {
_ai_char = ai_ch;
_seek_position = pos;
_seek_weight = seek_wt;
_seek_direction = _seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
_seek_direction.normalize();
_seek_done = false;
}
Seek::~Seek() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_seek
// Description : This function performs the seek and returns a seek force which is used
// in the calculate_prioritized function.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Seek::do_seek() {
double target_distance = (_seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length();
if(int(target_distance) == 0) {
_seek_done = true;
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
_ai_char->_steering->turn_off("seek");
return(LVecBase3(0.0, 0.0, 0.0));
}
LVecBase3 desired_force = _seek_direction * _ai_char->_movt_force;
return(desired_force);
}

View file

@ -0,0 +1,41 @@
////////////////////////////////////////////////////////////////////////
// Filename : seek.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _SEEK_H
#define _SEEK_H
#include "aiGlobals.h"
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Seek {
public:
AICharacter *_ai_char;
LVecBase3 _seek_position;
float _seek_weight;
LVecBase3 _seek_direction;
bool _seek_done;
LVecBase3 _seek_accum_force;
Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt = 1.0);
Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt = 1.0);
~Seek();
LVecBase3 do_seek();
};
#endif

View file

@ -0,0 +1,142 @@
////////////////////////////////////////////////////////////////////////
// Filename : wander.cxx
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "wander.h"
/////////////////////////////////////////////////////////////////////////////////
//
// Function : rand_float
// Description : This function creates a random float point number
/////////////////////////////////////////////////////////////////////////////////
double rand_float() {
const static double rand_max = 0x7fff;
return ((rand()) / (rand_max + 1.0));
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : random_clamped
// Description : This function returns a random floating point number in the range
// -1 to 1.
/////////////////////////////////////////////////////////////////////////////////
double random_clamped() {
return (rand_float() - rand_float());
}
Wander::Wander(AICharacter *ai_ch, double wander_radius,int flag, double aoe, float wander_weight) {
_ai_char = ai_ch;
_wander_radius = wander_radius ;
_wander_weight = wander_weight;
double theta = rand_float() * 2 * 3.14159;
double si = rand_float() * 3.14159;
_flag = flag;
// Area around which the character should wander
_area_of_effect = aoe;
_init_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render());
// _flag is used by Wander to wander in a given axis
// Value 0 - XY axes wander
// Value 1 - YZ axes wander
// Value 2 - XZ axes wander
// Value 3 - XYZ axes wander
// default is XY axes
switch(_flag) {
case 0: {
_wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
break;
}
case 1: {
_wander_target = LVecBase3(0, _wander_radius * cos(theta), _wander_radius * sin(theta));
break;
}
case 2: {
_wander_target = LVecBase3(_wander_radius * cos(theta), 0, _wander_radius * sin(theta));
break;
}
case 3: {
_wander_target = LVecBase3(_wander_radius * sin(theta) * cos(si), _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta));
break;
}
default: {
_wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
break;
}
}
}
Wander::~Wander() {
}
/////////////////////////////////////////////////////////////////////////////////
//
// Function : do_wander
// Description : This function performs the wander and returns the wander force which is used
// in the calculate_prioritized function.
// This function is not to be used by the user.
/////////////////////////////////////////////////////////////////////////////////
LVecBase3 Wander::do_wander() {
LVecBase3 present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render());
// Create the random slices to enable random movement of wander for x,y,z respectively
double time_slice_1 = random_clamped() * 1.5;
double time_slice_2 = random_clamped() * 1.5;
double time_slice_3 = random_clamped() * 1.5;
switch(_flag) {
case 0: {
_wander_target += LVecBase3(time_slice_1, time_slice_2, 0);
break;
}
case 1: {
_wander_target += LVecBase3(0, time_slice_1, time_slice_2);
break;
}
case 2: {
_wander_target += LVecBase3(time_slice_1, 0, time_slice_2);
break;
}
case 3: {
_wander_target += LVecBase3(time_slice_1, time_slice_2, time_slice_3);
break;
}
default: {
_wander_target = LVecBase3(time_slice_1, time_slice_2, 0);
}
}
_wander_target.normalize();
_wander_target *= _wander_radius;
LVecBase3 target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
target.normalize();
// Project wander target onto global space
target = _wander_target + target;
LVecBase3 desired_target = present_pos + target;
LVecBase3 desired_force = desired_target - _ai_char->get_node_path().get_pos() ;
desired_force.normalize();
desired_force *= _ai_char->_movt_force;
double distance = (present_pos - _init_pos).length();
if(_area_of_effect > 0 && distance > _area_of_effect) {
LVecBase3 direction = present_pos - _init_pos;
direction.normalize();
desired_force = - direction * _ai_char->_movt_force;
LVecBase3 dirn = _ai_char->_steering->_steering_force;
dirn.normalize();
_ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
}
return desired_force;
}

View file

@ -0,0 +1,38 @@
////////////////////////////////////////////////////////////////////////
// Filename : wander.h
// Created by : Deepak, John, Navin
// Date : 24 Oct 09
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef _WANDER_H
#define _WANDER_H
#include "aiCharacter.h"
class AICharacter;
class EXPCL_PANDAAI Wander {
public:
AICharacter *_ai_char;
double _wander_radius;
LVecBase3 _wander_target;
float _wander_weight;
int _flag;
LVecBase3 _init_pos;
double _area_of_effect;
Wander(AICharacter *ai_ch, double wander_radius, int flag, double aoe, float wander_weight);
LVecBase3 do_wander();
~Wander();
};
#endif

View file

@ -0,0 +1,13 @@
#define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
p3dtoolutil:c p3dtoolbase:c p3dtool:m p3prc:c
#begin lib_target
#define TARGET p3contribbase
#define SOURCES \
contribbase.cxx contribbase.h contribsymbols.h \
#define INSTALL_HEADERS \
contribbase.h contribbase.h
#end lib_target

View file

@ -0,0 +1,15 @@
// Filename: contribbase.cxx
// Created by: rdb (26Apr10)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "contribbase.h"

View file

@ -0,0 +1,26 @@
/* Filename: contribbase.h
* Created by: rdb (30Dec09)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This file is included at the beginning of every header file and/or
C or C++ file. It must be compilable for C as well as C++ files,
so no C++-specific code or syntax can be put here. */
#ifndef CONTRIBBASE_H
#define CONTRIBBASE_H
#include "pandabase.h"
#include "contribsymbols.h"
#endif

View file

@ -0,0 +1,33 @@
/* Filename: contribsymbols.h
* Created by: rdb (30Dec09)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef CONTRIBSYMBOLS_H
#define CONTRIBSYMBOLS_H
/* See dtoolsymbols.h for a rant on the purpose of this file. */
/* Note that the symbols declared in this file appear in alphabetical
order. Also note that we must use C-style comments only here, not
C++-style comments, since this file is occasionally included by a C
file. */
#ifdef BUILDING_PANDAAI
#define EXPCL_PANDAAI EXPORT_CLASS
#define EXPTP_PANDAAI EXPORT_TEMPL
#else
#define EXPCL_PANDAAI IMPORT_CLASS
#define EXPTP_PANDAAI IMPORT_TEMPL
#endif
#endif

View file

@ -0,0 +1,2 @@
/build
/dist

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
python setup.py py2exe
explorer .\dist

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

View file

@ -0,0 +1,4 @@
from distutils.core import setup
import py2exe
setup(console=['Panda3DToolsGUI.py'])

View file

@ -0,0 +1,188 @@
#################################################################
# AlignTool.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
class AlignTool(AppShell):
#################################################################
# AlignTool(AppShell)
#################################################################
appversion = '1.0'
appname = 'Align Tool'
frameWidth = 220
frameHeight = 330
frameIniPosX = 250
frameIniPosY = 250
padx = 0
pady = 0
def __init__(self, list = [], parent = None, nodePath = None, **kw):
# Keep nodePath Data
self.nodePath = nodePath
self.targetList = list
self.targetName = None
# Rename App
self.appname += (' '+self.nodePath.getName())
# Define the megawidget options.
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
if parent == None:
self.parent = Toplevel()
AppShell.__init__(self, self.parent)
self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY))
self.initialiseoptions(AlignTool)
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def appInit(self):
return
def createInterface(self):
# The interior of the toplevel panel
interior = self.interior()
mainFrame = Frame(interior)
frame = Frame(mainFrame)
self.nameBox = self.createcomponent(
'Align Target', (), None,
Pmw.ComboBox, (frame,),
labelpos = W, label_text='Target Node:', entry_width = 20, entry_state = DISABLED,
selectioncommand = self.setTargetNode,
scrolledlist_items = self.targetList)
self.nameBox.pack(side=LEFT)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
group = Pmw.Group(mainFrame, tag_text = 'Setting')
group.pack(side=TOP, fill = 'both', expand = 1,pady=5)
groupFrame = group.interior()
# X and H checkbox
frame = Frame(groupFrame)
self.alignXVar = IntVar()
self.alignXVar.set(False)
self.alignXButton = Checkbutton(
frame,
text = ': Align X',
variable = self.alignXVar)
self.alignXButton.pack(side=LEFT, expand=False)
self.alignHVar = IntVar()
self.alignHVar.set(False)
self.alignHButton = Checkbutton(
frame,
text = ': Align H',
variable = self.alignHVar)
self.alignHButton.pack(side=RIGHT, expand=False)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
groupFrame.pack(side=TOP, fill = 'both', expand = 1,padx=5,pady=5)
frame = Frame(mainFrame)
Button(frame, text='Align', width = 13, command=self.Align_press).pack(side=LEFT)
Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=RIGHT)
frame.pack(side=BOTTOM, fill = X, expand = 1,pady=5)
# Y and P checkbox
frame = Frame(groupFrame)
self.alignYVar = IntVar()
self.alignYVar.set(False)
self.alignYButton = Checkbutton(
frame,
text = ': Align Y',
variable = self.alignYVar)
self.alignYButton.pack(side=LEFT, expand=False)
self.alignPVar = IntVar()
self.alignPVar.set(False)
self.alignPButton = Checkbutton(
frame,
text = ': Align P',
variable = self.alignPVar)
self.alignPButton.pack(side=RIGHT, expand=False)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
# Z and R checkbox
frame = Frame(groupFrame)
self.alignZVar = IntVar()
self.alignZVar.set(False)
self.alignZButton = Checkbutton(
frame,
text = ': Align Z',
variable = self.alignZVar)
self.alignZButton.pack(side=LEFT, expand=False)
self.alignRVar = IntVar()
self.alignRVar.set(False)
self.alignRButton = Checkbutton(
frame,
text = ': Align R',
variable = self.alignRVar)
self.alignRButton.pack(side=RIGHT, expand=False)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
# Scale
frame = Frame(groupFrame)
Label(frame,text='Align Scale:').pack(side=LEFT)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
frame = Frame(groupFrame)
self.alignSXVar = IntVar()
self.alignSXVar.set(False)
self.alignSXButton = Checkbutton(
frame,
text = ': X',
variable = self.alignSXVar)
self.alignSXButton.pack(side=LEFT, expand=False)
self.alignSYVar = IntVar()
self.alignSYVar.set(False)
self.alignSYButton = Checkbutton(
frame,
text = ': Y',
variable = self.alignSYVar)
self.alignSYButton.pack(side=LEFT, expand=False)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
self.alignSZVar = IntVar()
self.alignSZVar.set(False)
self.alignSZButton = Checkbutton(
frame,
text = ': Z',
variable = self.alignSZVar)
self.alignSZButton.pack(side=LEFT, expand=False)
frame.pack(side=TOP, fill = X, expand = 1,pady=5)
mainFrame.pack(fill = 'both', expand = 1,padx=7,pady=7)
def createMenuBar(self):
self.menuBar.destroy()
def onDestroy(self, event):
messenger.send('ALW_close', [self.nodePath.getName()])
'''
If you have open any thing, please rewrite here!
'''
pass
###############################
def ok_press(self):
#################################################################
# ok_press(self)
# Callback function
# This function will be called when user click on the "OK" button on the window.
#################################################################
self.quit()
def Align_press(self):
list = [self.alignXVar.get(), self.alignYVar.get(), self.alignZVar.get(),
self.alignHVar.get(), self.alignPVar.get(), self.alignRVar.get(),
self.alignSXVar.get(), self.alignSYVar.get(), self.alignSZVar.get()]
if self.targetName != None:
messenger.send('ALW_align', [self.nodePath, self.targetName, list])
return
def setTargetNode(self,name=None):
self.targetName = name
return

View file

@ -0,0 +1,59 @@
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
import Pmw
class MetadataPanel(AppShell,Pmw.MegaWidget):
appversion = '1.0'
appname = 'Metadata Panel'
frameWidth = 400
frameHeight = 400
padx = 0
pady = 0
usecommandarea = 0
usestatusarea = 0
Metatag=""
Metanode=None
tag_text=None
def __init__(self,nodePath,parent=None,**kw):
# Initialise superclass
Pmw.MegaWidget.__init__(self, parent)
# Define the megawidget options.
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
self.Metanode=nodePath
if(nodePath.hasTag("Metadata")):
self.Metatag=self.Metanode.getTag("Metadata")
if parent == None:
self.parent = Toplevel()
AppShell.__init__(self, self.parent)
self.parent.resizable(False,False)
def appInit(self):
print "Metadata Panel"
def createInterface(self):
interior = self.interior()
mainFrame = Frame(interior)
tag_label=Label (mainFrame,text="Enter Metadata",font=('MSSansSerif', 15),
relief = RIDGE, borderwidth=5)
tag_label.pack()
source=StringVar()
source.set(self.Metatag)
self.tag_text=Entry(mainFrame, width=10,textvariable=source)
self.tag_text.pack()
set_button=Button(mainFrame, text='Set Metadata',font=('MSSansSerif', 15),
relief = RIDGE, borderwidth=5, command= lambda:self.SetIt())
set_button.pack()
mainFrame.pack(fill = 'both', expand = 1)
def SetIt(self):
self.Metanode.setTag("Metadata",self.tag_text.get())

View file

@ -0,0 +1,458 @@
#################################################################
# sideWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
from direct.tkwidgets.AppShell import AppShell
from direct.tkwidgets.VectorWidgets import ColorEntry
from direct.showbase.TkGlobal import spawnTkLoop
import seSceneGraphExplorer
from Tkinter import Frame, IntVar, Checkbutton, Toplevel
import Pmw, Tkinter
class sideWindow(AppShell):
#################################################################
# sideWindow(AppShell)
# This class will open a side window wich contains a scene graph and
# a world setting page.
#################################################################
appversion = '1.0'
appname = 'Navigation Window'
frameWidth = 325
frameHeight = 580
frameIniPosX = 0
frameIniPosY = 110
padx = 0
pady = 0
lightEnable = 0
ParticleEnable = 0
basedriveEnable = 0
collision = 0
backface = 0
texture = 1
wireframe = 0
enableBaseUseDrive = 0
def __init__(self, worldColor,lightEnable,ParticleEnable, basedriveEnable,collision,
backface, texture, wireframe, grid, widgetVis, enableAutoCamera, parent = None, nodePath = render, **kw):
self.worldColor = worldColor
self.lightEnable = lightEnable
self.ParticleEnable = ParticleEnable
self.basedriveEnable = basedriveEnable
self.collision = collision
self.backface = backface
self.texture = texture
self.wireframe = wireframe
self.grid = grid
self.enableAutoCamera = enableAutoCamera
self.widgetVis = widgetVis
# Define the megawidget options.
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
if parent == None:
self.parent = Toplevel()
else:
self.parent = parent
AppShell.__init__(self, self.parent)
self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY))
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def appInit(self):
print '----SideWindow is Initialized!!'
def createInterface(self):
# The interior of the toplevel panel
interior = self.interior()
mainFrame = Frame(interior)
## Creat NoteBook
self.notebookFrame = Pmw.NoteBook(mainFrame)
self.notebookFrame.pack(fill=Tkinter.BOTH,expand=1)
sgePage = self.notebookFrame.add('Tree Graph')
envPage = self.notebookFrame.add('World Setting')
self.notebookFrame['raisecommand'] = self.updateInfo
## Tree Grapgh Page
self.SGE = seSceneGraphExplorer.seSceneGraphExplorer(
sgePage, nodePath = render,
scrolledCanvas_hull_width = 270,
scrolledCanvas_hull_height = 570)
self.SGE.pack(fill = Tkinter.BOTH, expand = 0)
## World Setting Page
envPage = Frame(envPage)
pageFrame = Frame(envPage)
self.LightingVar = IntVar()
self.LightingVar.set(self.lightEnable)
self.LightingButton = Checkbutton(
pageFrame,
text = 'Enable Lighting',
variable = self.LightingVar,
command = self.toggleLights)
self.LightingButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.CollisionVar = IntVar()
self.CollisionVar.set(self.collision)
self.CollisionButton = Checkbutton(
pageFrame,
text = 'Show Collision Object',
variable = self.CollisionVar,
command = self.showCollision)
self.CollisionButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.ParticleVar = IntVar()
self.ParticleVar.set(self.ParticleEnable)
self.ParticleButton = Checkbutton(
pageFrame,
text = 'Show Particle Dummy',
variable = self.ParticleVar,
command = self.enableParticle)
self.ParticleButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.baseUseDriveVar = IntVar()
self.baseUseDriveVar.set(self.basedriveEnable)
self.baseUseDriveButton = Checkbutton(
pageFrame,
text = 'Enable base.usedrive',
variable = self.baseUseDriveVar,
command = self.enablebaseUseDrive)
self.baseUseDriveButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.backfaceVar = IntVar()
self.backfaceVar.set(self.backface)
self.backfaceButton = Checkbutton(
pageFrame,
text = 'Enable BackFace',
variable = self.backfaceVar,
command = self.toggleBackface)
self.backfaceButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.textureVar = IntVar()
self.textureVar.set(self.texture)
self.textureButton = Checkbutton(
pageFrame,
text = 'Enable Texture',
variable = self.textureVar,
command = self.toggleTexture)
self.textureButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.wireframeVar = IntVar()
self.wireframeVar.set(self.wireframe)
self.wireframeButton = Checkbutton(
pageFrame,
text = 'Enable Wireframe',
variable = self.wireframeVar,
command = self.toggleWireframe)
self.wireframeButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.gridVar = IntVar()
self.gridVar.set(self.grid)
self.gridButton = Checkbutton(
pageFrame,
text = 'Enable Grid',
variable = self.gridVar,
command = self.toggleGrid)
self.gridButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.widgetVisVar = IntVar()
self.widgetVisVar.set(self.widgetVis)
self.widgetVisButton = Checkbutton(
pageFrame,
text = 'Enable WidgetVisible',
variable = self.widgetVisVar,
command = self.togglewidgetVis)
self.widgetVisButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.enableAutoCameraVar = IntVar()
self.enableAutoCameraVar.set(self.enableAutoCamera)
self.enableAutoCameraButton = Checkbutton(
pageFrame,
text = 'Enable Auto Camera Movement for Loading Objects',
variable = self.enableAutoCameraVar,
command = self.toggleAutoCamera)
self.enableAutoCameraButton.pack(side=Tkinter.LEFT, expand=False)
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
pageFrame = Frame(envPage)
self.backgroundColor = ColorEntry(
pageFrame, text = 'BG Color', value=self.worldColor)
self.backgroundColor['command'] = self.setBackgroundColorVec
self.backgroundColor['resetValue'] = [0,0,0,0]
self.backgroundColor.pack(side=Tkinter.LEFT, expand=False)
self.bind(self.backgroundColor, 'Set background color')
pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
envPage.pack(expand=False)
## Set all stuff done
self.notebookFrame.setnaturalsize()
mainFrame.pack(fill = 'both', expand = 1)
def createMenuBar(self):
# We don't need menu bar here.
self.menuBar.destroy()
def onDestroy(self, event):
#################################################################
# onDestroy(self, event)
# This function will be called when user closed the side window.
# Here we will send out a message with whole data set we will need
# for the next time user open the side window.
#################################################################
messenger.send('SW_close',[self.worldColor,
self.lightEnable,
self.ParticleEnable,
self.basedriveEnable,
self.collision,
self.backface,
self.texture,
self.wireframe,
self.grid,
self.widgetVis,
self.enableAutoCamera])
'''
If you have open any thing, please rewrite here!
'''
pass
###############################
def updateInfo(self, page = 'Tree Graph'):
#################################################################
# updateInfo(self, page = 'Tree Graph')
# This function will be called when each time user change the main
# page of the window.
# What it dose is to call right function to restore the data for current selected page.
#################################################################
if page=='Tree Graph':
self.updateTreeGraph()
elif page == 'World Setting':
self.updateWorldSetting()
def updateTreeGraph(self):
#################################################################
# updateTreeGraph(self)
# When scene graoh page has been opend, call sceneGraphExplorer to
# updata the tree.
#################################################################
self.SGE.update()
pass
def updateWorldSetting(self):
#################################################################
# updateWorldSetting(self)
# When world setting page has been selected, this function will
# reset those check box in the page to reflect the current world setting.
#################################################################
self.LightingVar.set(self.lightEnable)
self.CollisionVar.set(self.collision)
self.ParticleVar.set(self.ParticleEnable)
self.baseUseDriveVar.set(self.basedriveEnable)
self.backgroundColor.set(value = self.worldColor)
pass
def toggleLights(self):
#################################################################
# toggleLights(self)
# send out a message to let sceneEditor know we need to toggle the light.
# Then, sceneEditor will pass the message to dataHolder to disable/enable
# the lights. (lightManager is inside the dataHolder)
#################################################################
self.lightEnable = (self.lightEnable+1)%2
messenger.send('SW_lightToggle')
pass
def showCollision(self):
#################################################################
# showCollision(self)
# This function will send out a message to sceneEditor to toggle
# the visibility of collision objects.
#################################################################
self.collision = (self.collision+1)%2
messenger.send('SW_collisionToggle', [self.collision])
pass
def enableParticle(self):
#################################################################
# enableParticle(self)
# This function will send out a message to sceneEditor to toggle
# the visibility of particle objects.
#################################################################
self.ParticleEnable = (self.ParticleEnable+1)%2
messenger.send('SW_particleToggle', [self.ParticleEnable])
pass
def enablebaseUseDrive(self):
#################################################################
# enablebaseUseDrive(self)
# This function will toggle the usage of base.useDrive.
# Well, it may not usefull at all.
#
# We won't send out any message in this time to notice
# the sceneEditor this event happend.
# In the other hand, we will restore it back when
# the side window has been closed.
#
#################################################################
if self.enableBaseUseDrive==0:
print 'Enabled'
base.useDrive()
self.enableBaseUseDrive = 1
else:
print 'disabled'
#base.useTrackball()
base.disableMouse()
self.enableBaseUseDrive = 0
self.basedriveEnable = (self.basedriveEnable+1)%2
pass
def toggleBackface(self):
#################################################################
# toggleBackface(self)
# This function will toggle the back face setting. so it will
# render the polygon with two sides.
#################################################################
base.toggleBackface()
self.backface = (self.backface+1)%2
return
def toggleBackfaceFromMainW(self):
#################################################################
# toggleBackfaceFromMainW(self)
# This function is called by sceneEditor when user used hot key
# to toggle the back face setting in the main panda window.
# In here we will only reset the flag and reset the state of
# check box
#################################################################
self.backface = (self.backface+1)%2
self.backfaceButton.toggle()
return
def toggleTexture(self):
#################################################################
# toggleTexture(self)
# This function will toggle the txture using option for the whole scene.
#################################################################
base.toggleTexture()
self.texture = (self.texture+1)%2
return
def toggleTextureFromMainW(self):
#################################################################
# toggleTextureFromMainW(self)
# This function is called by sceneEditor when user used hot key
# to toggle the texture usage from the main panda window.
# In here we will only reset the flag and reset the state of
# check box
#################################################################
self.texture = (self.texture+1)%2
self.textureButton.toggle()
return
def toggleWireframe(self):
#################################################################
# toggleWireframe(self)
# This function will toggle the wire frame mode.
#################################################################
base.toggleWireframe()
self.wireframe = (self.wireframe+1)%2
return
def toggleWireframeFromMainW(self):
#################################################################
# toggleWireframeFromMainW(self)
# This function is called by sceneEditor when user used hot key
# to toggle the wire frame mode in the main panda window.
# In here we will only reset the flag and reset the state of
# check box
#################################################################
self.wireframe = (self.wireframe+1)%2
self.wireframeButton.toggle()
return
def toggleGrid(self):
#################################################################
# toggleGrid(self)
# This function will toggle the usage of the grid.
#################################################################
self.grid = (self.grid+1)%2
if self.grid==1:
SEditor.grid.enable()
else:
SEditor.grid.disable()
def togglewidgetVis(self):
#################################################################
# togglewidgetVis(self)
# This function will toggle the visibility of the widget of the grid.
#################################################################
self.widgetVis = (self.widgetVis+1)%2
SEditor.toggleWidgetVis()
if SEditor.widget.fActive:
messenger.send('shift-f')
return
def toggleWidgetVisFromMainW(self):
#################################################################
# toggleWidgetVisFromMainW(self)
# This function is called by sceneEditor when user used hot key
# to toggle the visibility of widgets ('v') from the main panda window.
# In here we will only reset the flag and reset the state of
# check box
#################################################################
self.widgetVis = (self.widgetVis+1)%2
self.widgetVisButton.toggle()
return
def setBackgroundColorVec(self,color):
#################################################################
# setBackgroundColorVec(self,color)
# Call back function
# This will be called from the colorEntry on the world setting page.
# The "color" here is a list containing three integer data, R, G and B.
#################################################################
base.setBackgroundColor(color[0]/255.0,
color[1]/255.0,
color[2]/255.0)
self.worldColor = [color[0],color[1],color[2],0]
def toggleAutoCamera(self):
#################################################################
# toggleAutoCamera(self)
# This function will toggle the usage of the auto-camera movement
# when user loaded model or actor into the scene.
#################################################################
self.enableAutoCamera = (self.enableAutoCamera+1)%2
SEditor.toggleAutoCamera()
return
def selectPage(self,page='Tree Graph'):
#################################################################
#################################################################
self.notebookFrame.selectpage(page)

View file

@ -0,0 +1,2 @@
// For now, since we are not installing Python files, this file can
// remain empty.

View file

@ -0,0 +1,302 @@
#################################################################
# collisionWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
from seColorEntry import *
from direct.tkwidgets import VectorWidgets
from direct.tkwidgets import Floater
from direct.tkwidgets import Slider
from Tkinter import *
import string, math, types
from pandac.PandaModules import *
class collisionWindow(AppShell):
#################################################################
# This will open a talk window for user to set the collision object
# In here, we won't finish the whole process to generate the
# collision object, half of process will be finished by dataHolder.
#################################################################
# Override class variables
appname = 'Creating Collision Object'
frameWidth = 600
frameHeight = 300
widgetsDict = {}
# Define the types of collision we take care here
collisionType = ['collisionPolygon',
'collisionSphere',
'collisionSegment',
'collisionRay']
def __init__(self, nodePath, parent = None, **kw):
self.nodePath = nodePath
self.objType = 'collisionSphere' # set default type to Collision Sphere
INITOPT = Pmw.INITOPT
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(collisionWindow)
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
menuBar = self.menuBar
self.menuBar.destroy()
# Create a frame to hold all stuff
mainFrame = Frame(interior)
frame = Frame(mainFrame)
self.collisionTypeEntry = self.createcomponent(
'Collision Type', (), None,
Pmw.ComboBox, (frame,),
labelpos = W, label_text='Collision Object Type:', entry_width = 20,
selectioncommand = self.setObjectType,
scrolledlist_items = self.collisionType)
self.collisionTypeEntry.pack(side=LEFT, padx=3)
label = Label(frame, text='Parent NodePath: '+ self.nodePath.getName(), font=('MSSansSerif', 12),
relief = RIDGE)
label.pack(side=LEFT,expand=0,fill=X, padx=20)
frame.pack(side=TOP, fill=X, expand=True, padx=3)
self.collisionTypeEntry.selectitem('collisionSphere', setentry=True)
self.inputZone = Pmw.Group(mainFrame, tag_pyclass = None)
self.inputZone.pack(fill='both',expand=1)
settingFrame = self.inputZone.interior()
############################################
# Notebook pages for specific object setting
############################################
self.objNotebook = Pmw.NoteBook(settingFrame, tabpos = None,
borderwidth = 0)
PolygonPage = self.objNotebook.add('Polygon')
SpherePage = self.objNotebook.add('Sphere')
SegmentPage = self.objNotebook.add('Segment')
RayPage = self.objNotebook.add('Ray')
self.objNotebook.selectpage('Sphere')
# Put this here so it isn't called right away
self.objNotebook['raisecommand'] = self.updateObjInfo
# Polygon object setting
Interior = Frame(PolygonPage)
label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!')
label.pack(side=LEFT,expand=0,fill=X, padx=1)
Interior.pack(side=TOP, expand=0,fill=X)
self.createPosEntry(PolygonPage, catagory='Polygon', id='Point A')
self.createPosEntry(PolygonPage, catagory='Polygon', id='Point B')
self.createPosEntry(PolygonPage, catagory='Polygon', id='Point C')
# Sphere object setting
Interior = Frame(SpherePage)
label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!')
label.pack(side=LEFT,expand=0,fill=X, padx=1)
Interior.pack(side=TOP, expand=0,fill=X)
self.createPosEntry(SpherePage, catagory='Sphere', id='Center Point')
self.createEntryField(SpherePage,catagory='Sphere', id='Size',
value = 1.0,
command = None,
initialState='normal',
side = 'top')
# Segment object setting
Interior = Frame(SegmentPage)
label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!')
label.pack(side=LEFT,expand=0,fill=X, padx=1)
Interior.pack(side=TOP, expand=0,fill=X)
self.createPosEntry(SegmentPage, catagory='Segment', id='Point A')
self.createPosEntry(SegmentPage, catagory='Segment', id='Point B')
# Ray object setting
Interior = Frame(RayPage)
label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!')
label.pack(side=LEFT,expand=0,fill=X, padx=1)
Interior.pack(side=TOP, expand=0,fill=X)
self.createPosEntry(RayPage, catagory='Ray', id='Origin')
self.createPosEntry(RayPage, catagory='Ray', id='Direction')
self.objNotebook.setnaturalsize()
self.objNotebook.pack(expand = 1, fill = BOTH)
self.okButton = Button(mainFrame, text="OK", command=self.okPress,width=10)
self.okButton.pack(fill=BOTH,expand=0,side=RIGHT)
mainFrame.pack(expand=1, fill = BOTH)
def onDestroy(self, event):
messenger.send('CW_close')
'''
If you have open any thing, please rewrite here!
'''
pass
def setObjectType(self, typeName = 'collisionSphere'):
#################################################################
# setObjectType(self, typeName = 'collisionSphere')
# Call back function
# This function will be called when user select target collision
# type on the combo box on the panel.
# Basically, this function's job is to switch the notebook page to right one.
#################################################################
self.objType = typeName
if self.objType=='collisionPolygon':
self.objNotebook.selectpage('Polygon')
elif self.objType=='collisionSphere':
self.objNotebook.selectpage('Sphere')
elif self.objType=='collisionSegment':
self.objNotebook.selectpage('Segment')
elif self.objType=='collisionRay':
self.objNotebook.selectpage('Ray')
return
def updateObjInfo(self, page=None):
#################################################################
# Nothing. Unlike in the lighting panel, we don't have to keep data
# once user switch the page.
#################################################################
return
def okPress(self):
#################################################################
# okPress(self)
# This function will be called when user click on the Ok button.
# Then this function will collect all parameters that we need to create
# a collision Object from the panel and generate the colision Object.
# In the last, it will send the object out with a message to dataHolder to
# put the object into a CollisionNode and attach it to the target nodePath
#################################################################
collisionObject = None
print self.objType
if self.objType=='collisionPolygon':
pointA = Point3(float(self.widgetDict['PolygonPoint A'][0]._entry.get()),
float(self.widgetDict['PolygonPoint A'][1]._entry.get()),
float(self.widgetDict['PolygonPoint A'][2]._entry.get()))
pointB = Point3(float(self.widgetDict['PolygonPoint B'][0]._entry.get()),
float(self.widgetDict['PolygonPoint B'][1]._entry.get()),
float(self.widgetDict['PolygonPoint B'][2]._entry.get()))
pointC = Point3(float(self.widgetDict['PolygonPoint C'][0]._entry.get()),
float(self.widgetDict['PolygonPoint C'][1]._entry.get()),
float(self.widgetDict['PolygonPoint C'][2]._entry.get()))
collisionObject = CollisionPolygon(pointA, pointB, pointC)
elif self.objType=='collisionSphere':
collisionObject = CollisionSphere(float(self.widgetDict['SphereCenter Point'][0]._entry.get()),
float(self.widgetDict['SphereCenter Point'][1]._entry.get()),
float(self.widgetDict['SphereCenter Point'][2]._entry.get()),
float(self.widgetDict['SphereSize'].getvalue()))
elif self.objType=='collisionSegment':
pointA = Point3(float(self.widgetDict['SegmentPoint A'][0]._entry.get()),
float(self.widgetDict['SegmentPoint A'][1]._entry.get()),
float(self.widgetDict['SegmentPoint A'][2]._entry.get()))
pointB = Point3(float(self.widgetDict['SegmentPoint B'][0]._entry.get()),
float(self.widgetDict['SegmentPoint B'][1]._entry.get()),
float(self.widgetDict['SegmentPoint B'][2]._entry.get()))
collisionObject = CollisionSegment()
collisionObject.setPointA(pointA)
collisionObject.setFromLens(base.cam.node(), Point2( -1, 1 )) ## You must set up the camera lensNode before you set point B....
collisionObject.setPointB(pointB)
elif self.objType=='collisionRay':
point = Point3(float(self.widgetDict['RayOrigin'][0]._entry.get()),
float(self.widgetDict['RayOrigin'][1]._entry.get()),
float(self.widgetDict['RayOrigin'][2]._entry.get()))
vector = Vec3(float(self.widgetDict['RayDirection'][0]._entry.get()),
float(self.widgetDict['RayDirection'][1]._entry.get()),
float(self.widgetDict['RayDirection'][2]._entry.get()))
print vector, point
collisionObject = CollisionRay()
collisionObject.setOrigin(point)
collisionObject.setDirection(vector)
#collisionObject.setFromLens(base.cam.node(), Point2( -1, 1 )) ## You must set up the camera lensNode before you set up others...
if self.objType=='collisionPolygon':
messenger.send('CW_addCollisionObj', [collisionObject, self.nodePath, pointA, pointB, pointC])
else:
messenger.send('CW_addCollisionObj', [collisionObject, self.nodePath])
self.quit()
return
def createPosEntry(self, contentFrame, catagory, id):
posInterior = Frame(contentFrame)
label = Label(posInterior, text=id+':')
label.pack(side=LEFT,expand=0,fill=X, padx=1)
self.posX = self.createcomponent('posX'+catagory+id, (), None,
Floater.Floater, (posInterior,),
text = 'X', relief = FLAT,
value = 0.0,
entry_width = 6)
self.posX.pack(side=LEFT,expand=0,fill=X, padx=1)
self.posY = self.createcomponent('posY'+catagory+id, (), None,
Floater.Floater, (posInterior,),
text = 'Y', relief = FLAT,
value = 0.0,
entry_width = 6)
self.posY.pack(side=LEFT, expand=0,fill=X, padx=1)
self.posZ = self.createcomponent('posZ'+catagory+id, (), None,
Floater.Floater, (posInterior,),
text = 'Z', relief = FLAT,
value = 0.0,
entry_width = 6)
self.posZ.pack(side=LEFT, expand=0,fill=X, padx=1)
self.widgetDict[catagory+id]=[self.posX, self.posY, self.posZ]
posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3)
return
def createEntryField(self, parent, catagory, id, value,
command, initialState, labelWidth = 6,
side = 'left', fill = X, expand = 0,
validate = None,
defaultButton = False, buttonText = 'Default',defaultFunction = None ):
frame = Frame(parent)
widget = Pmw.EntryField(frame, labelpos='w', label_text = id+':',
value = value, entry_font=('MSSansSerif', 10),label_font=('MSSansSerif', 10),
modifiedcommand=command, validate = validate,
label_width = labelWidth)
widget.configure(entry_state = initialState)
widget.pack(side=LEFT)
self.widgetDict[catagory+id] = widget
if defaultButton and (defaultFunction!=None):
widget = Button(frame, text=buttonText, font=('MSSansSerif', 10), command = defaultFunction)
widget.pack(side=LEFT, padx=3)
self.widgetDict[catagory+id+'-'+'DefaultButton']=widget
frame.pack(side = side, fill = fill, expand = expand,pady=3)

View file

@ -0,0 +1,695 @@
#################################################################
# controllerWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
from direct.tkwidgets.AppShell import AppShell
from Tkinter import Frame, Label, Button
import string, Pmw, Tkinter
# Define the Category
KEYBOARD = 'Keyboard-'
TRACKER = 'Tarcker-'
class controllerWindow(AppShell):
#################################################################
# This will open a talk window for user to set the control mechanism
# In here, user can choose to control what object by keyboard or other inputs.
#################################################################
# Override class variables
appname = 'Controller Panel'
frameWidth = 500
frameHeight = 500
widgetsDict = {} # category-id : widget obj
# setup the type of controller we handle here.
controllerList = ['Keyboard',
'Tracker']
# Default Keyboard setting
keyboardMapDict = {}
keyboardSpeedDict = {}
def __init__(self, listOfObj, controlType , dataList, parent = None, **kw):
if controlType == 'Keyboard':
self.nodePath = dataList[0] # Default setting -> mainly used for Keyboard control now.
self.nameOfNode = self.nodePath.getName()
self.controllType = 'Keyboard'
self.keyboardMapDict.clear()
self.keyboardMapDict = dataList[1]
self.keyboardSpeedDict.clear()
self.keyboardSpeedDict = dataList[2]
self.listOfObj = listOfObj
self.keepControl = False
INITOPT = Pmw.INITOPT
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(controllerWindow)
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
menuBar = self.menuBar
# We don't need menu bar here
self.menuBar.destroy()
# Create a frame to hold all stuff
mainFrame = Frame(interior)
# A comboBox to switch the controller
frame = Frame(mainFrame)
self.cotrollerTypeEntry = self.createcomponent(
'Controller Type', (), None,
Pmw.ComboBox, (frame,),
labelpos = Tkinter.W, label_text='Controller Type:', entry_width = 20,entry_state = Tkinter.DISABLED,
selectioncommand = self.setControllerType,
scrolledlist_items = self.controllerList)
self.cotrollerTypeEntry.pack(side=Tkinter.LEFT)
frame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=False, pady = 3)
self.cotrollerTypeEntry.selectitem('Keyboard', setentry=True)
self.inputZone = Pmw.Group(mainFrame, tag_pyclass = None)
self.inputZone.pack(fill='both',expand=1)
settingFrame = self.inputZone.interior()
###################################################
# Notebook pages for specific controller setting #
###################################################
self.contentWidge = self.createcomponent(
'scrolledFrame',
(), None,
Pmw.ScrolledFrame, (settingFrame,),
hull_width = 200, hull_height = 300,
usehullsize = 1)
self.contentFrame = self.contentWidge.component('frame')
self.contentWidge.pack(fill = 'both', expand = 1,padx = 3, pady = 5)
self.objNotebook = Pmw.NoteBook(self.contentFrame, tabpos = None,
borderwidth = 0)
keyboardPage = self.objNotebook.add('Keyboard')
tarckerPage = self.objNotebook.add('Tracker')
self.objNotebook.selectpage('Keyboard')
self.objNotebook.pack(side = Tkinter.TOP, fill='both',expand=False)
# Put this here so it isn't called right away
self.objNotebook['raisecommand'] = self.updateControlInfo
# Keyboard setting
assignFrame = Frame(keyboardPage)
Interior = Frame(assignFrame)
widget = self.createcomponent(
'Target Type', (), None,
Pmw.ComboBox, (Interior,),
labelpos = Tkinter.W, label_text='Target Object:', entry_width = 20, entry_state = Tkinter.DISABLED,
selectioncommand = self.setTargetObj,
scrolledlist_items = self.listOfObj)
widget.pack(side=Tkinter.LEFT, padx=3)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 5)
widget.selectitem(self.nameOfNode, setentry=True)
self.widgetsDict[KEYBOARD+'ObjList'] = widget
inputZone = Pmw.Group(assignFrame, tag_pyclass = None)
inputZone.pack(fill='both',expand=1)
settingFrame = inputZone.interior()
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Assign a Key For:').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True,pady = 6 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Forward :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Forward key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyForward'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyForward'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Forward Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedForward'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedForward'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Backward :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Backward key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyBackward'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyBackward'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Backward Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedBackward'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedBackward'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Right :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Right key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyRight'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyRight'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Right Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedRight'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedRight'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Left :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Left key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyLeft'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyLeft'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Left Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedLeft'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedLeft'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Up :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Up key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Up Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Down :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Down Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Turn Right:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Right key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyTurnRight'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyTurnRight'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Right Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedTurnRight'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedTurnRight'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Turn Left :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Left key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyTurnLeft'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyTurnLeft'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Left Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedTurnLeft'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedTurnLeft'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Turn UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn UP key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyTurnUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyTurnUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn UP Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedTurnUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedTurnUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Turn Down :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyTurnDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyTurnDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Turn Down Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedTurnDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedTurnDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Roll Right:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Roll Right key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyRollRight'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyRollRight'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Roll Right Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedRollRight'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedRollRight'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Roll Left :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Roll Left key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyRollLeft'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyRollLeft'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Roll Left Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedRollLeft'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedRollLeft'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale UP key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale UP Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Down Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale X UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale X UP key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleXUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleXUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale X UP Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleXUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleXUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale X Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale X Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleXDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleXDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Down X Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleXDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleXDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale Y UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Y UP key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleYUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleYUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Y UP Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleYUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleYUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale Y Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Y Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleYDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleYDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Down XY Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleYDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleYDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale Z UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Z UP key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleZUp'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleZUp'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Z UP Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleZUp'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleZUp'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
Interior = Frame(settingFrame)
widget = Label(Interior, text = 'Scale Z Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Z Down key', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardMapDict['KeyScaleZDown'],
labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'KeyScaleZDown'] = widget
widget = Label(Interior, text = ' ').pack(side=Tkinter.LEFT, expand = False)
widget = self.createcomponent(
'Scale Down Z Speed', (), None,
Pmw.EntryField, (Interior,),
value = self.keyboardSpeedDict['SpeedScaleZDown'],
labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
widget.pack(side=Tkinter.LEFT, expand = False)
self.widgetsDict[KEYBOARD+'SpeedScaleZDown'] = widget
widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
assignFrame.pack(side=Tkinter.TOP, expand=True, fill = Tkinter.X)
keyboardPage.pack(side=Tkinter.TOP, expand=True, fill = Tkinter.X)
####################################################################
####################################################################
# End of Keyboard control page
####################################################################
####################################################################
# Pack the mainFrame
frame = Frame(mainFrame)
widget = Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=Tkinter.RIGHT)
widget = Button(frame, text='Enable Control', width = 13, command=self.enableControl).pack(side=Tkinter.LEFT)
widget = Button(frame, text='Disable Control', width = 13, command=self.disableControl).pack(side=Tkinter.LEFT)
widget = Button(frame, text='Save & Keep', width = 13, command=self.saveKeepControl).pack(side=Tkinter.LEFT)
frame.pack(side = Tkinter.BOTTOM, expand=1, fill = Tkinter.X)
mainFrame.pack(expand=1, fill = Tkinter.BOTH)
def onDestroy(self, event):
# Check if user wish to keep the control after the window closed.
if not self.keepControl:
messenger.send('ControlW_controlDisable',[self.controllType])
messenger.send('ControlW_close')
'''
If you have open any thing, please rewrite here!
'''
pass
def ok_press(self):
####################################################################
# ok_press(self)
# After user clicked on "OK" button, this function will be called.
# This function will collect data from the panel and send it back to
# sceneEditor and close the window. It won't activate control at all.
####################################################################
if self.controllType=='Keyboard':
for index in self.keyboardMapDict:
self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue()
for index in self.keyboardSpeedDict:
self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue())
messenger.send('ControlW_controlSetting', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]])
self.quit()
return
def enableControl(self):
####################################################################
# enableControl(self)
# Call back function.
# THis function will be called each time when user clicks on the
# "Enable Control" button. This function will do pretty much
# the same thing with ok_press function, except that this function
# will activate the control process in sceneEditor.
# However, if user didn't click on the button "Keep ANd Save,"
# the control process will be terminated when user closed the panel.
####################################################################
if self.controllType=='Keyboard':
for index in self.keyboardMapDict:
self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue()
for index in self.keyboardSpeedDict:
self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue())
messenger.send('ControlW_controlEnable', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]])
return
def disableControl(self):
####################################################################
# disableControl(self)
# This function will send out a message to sceneEditor to stop the
# control task.
####################################################################
messenger.send('ControlW_controlDisable',[self.controllType])
return
def setControllerType(self, typeName = 'Keyboard'):
#################################################################
# setControllerType(self, typeName = 'Keyboard')
# Call back function
# This function will be called when user select the type of
# controller they want on the combo box on the panel.
# Basically, this function's job is to switch the notebook page to right one.
#################################################################
self.controllType = typeName
if self.controllType=='Keyboard':
self.objNotebook.selectpage('Keyboard')
elif self.controllType=='Tracker':
self.objNotebook.selectpage('Tracker')
return
def updateControlInfo(self, page=None):
#################################################################
# Nothing. Unlike in the lighting panel, we don't have to keep data
# once user switch the page.
#################################################################
return
def setTargetObj(self, name = None, tracker = None):
#################################################################
# setTargetObj(self, name = None)
# setup the target object we want to control
#################################################################
if tracker == None: # Call from Keyboard page.
if name=='camera':
self.nodePath = camera
else:
messenger.send('ControlW_require',[name])
return
def resetNameList(self, list, name = None, nodePath = None):
####################################################################
# resetNameList(self, list, name = None, nodePath = None)
# This function will be called be sceneEditor in order to reset the
# object list inside the panel.
# list here is a name list for all objects that can be set on control
# in the scene. If user has specify a name and a nodePath in, it will
# check if the name is equal to the name of current control target.
# If so, it will change the name showed on panel.
####################################################################
self.widgetsDict['Keyboard-ObjList'].setlist(list)
if name != None:
if self.nameOfNode == name:
self.nameOfNode = self.nodePath.getName()
self.widgetsDict['Keyboard-ObjList'].selectitem(self.nameOfNode, setentry=True)
return
def setNodePathIn(self, nodePath):
####################################################################
# setNodePathIn(self, nodePath)
# THis function will be called by sceneEditor.
# After we send out a message to sceneEditor in setTargetObj function,
# This function will be called by sceneEditor after we get the reference
# of target object from dataHolder.
# This function will keep the reference.
####################################################################
self.nodePath = nodePath
self.nameOfNode = self.nodePath.getName()
return
def saveKeepControl(self):
#################################################################
# saveKeepControl(self)
# When any time user has click on the "Save & Keep" button,
# This function will be called.
# This function will send out the message to sceneEditor to process
# the saving. Also, this function will change the "self.keepControl" flag.
# So, when user closes the window with control enabled, it will keep
# the control process runnning. otherwise program will disable the
# control automatically when panel has been closed.
#
# It doesn't mean that this function will automatically enable the
# control when user closed the window!!
# This flag will only decide that we will send out a "stopControl"
# message or not.
#
#################################################################
self.keepControl = True
if self.controllType=='Keyboard':
for index in self.keyboardMapDict:
self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue()
for index in self.keyboardSpeedDict:
self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue())
print self.nodePath
messenger.send('ControlW_saveSetting', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]])
return

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,172 @@
#################################################################
# duplicateWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
import seSceneGraphExplorer
class duplicateWindow(AppShell):
#################################################################
# duplicateWindow(AppShell)
# This class is used to create a dialog window
# for handling the "dupicate" command from the sceneEditor
#
# Notice!
# The actual duplicating process is not happending here!
# The one handle the process is dataHolder...
#################################################################
appversion = '1.0'
appname = 'Duplication'
frameWidth = 450
frameHeight = 320
frameIniPosX = 250
frameIniPosY = 250
padx = 0
pady = 0
def __init__(self, parent = None, nodePath = None, **kw):
# Define the megawidget options.
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
if parent == None:
self.parent = Toplevel()
AppShell.__init__(self, self.parent)
self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY))
self.nodePath = nodePath
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def appInit(self):
print '----SideWindow is Initialized!!'
def createInterface(self):
# The interior of the toplevel panel
interior = self.interior()
mainFrame = Frame(interior)
self.inputZone = Pmw.Group(mainFrame, tag_text='Offset setting')
self.inputZone.pack(fill='both',expand=1)
settingFrame = self.inputZone.interior()
Label(settingFrame,text=' X ').place(anchor=NW,x=110,y=15)
Label(settingFrame,text=' Y ').place(anchor=NW,x=205,y=15)
Label(settingFrame,text=' Z ').place(anchor=NW,x=295,y=15)
self.move_x = Pmw.EntryField(settingFrame,label_text='Move :',labelpos='w',value='0.0', validate=Pmw.realvalidator)
self.move_x.component('entry').config(width=10)
self.move_y = Pmw.EntryField(settingFrame,value='0.0', validate=Pmw.realvalidator)
self.move_y.component('entry').config(width=10)
self.move_z = Pmw.EntryField(settingFrame, value='0.0', validate=Pmw.realvalidator)
self.move_z.component('entry').config(width=10)
self.move_x.place(anchor=NW,x=50,y=40)
self.move_y.place(anchor=NW,x=185,y=40)
self.move_z.place(anchor=NW,x=275,y=40)
self.rotate_x = Pmw.EntryField(settingFrame,label_text='Rotate:',labelpos='w',value='0.0', validate=Pmw.realvalidator)
self.rotate_x.component('entry').config(width=10)
self.rotate_y = Pmw.EntryField(settingFrame,value='0.0', validate=Pmw.realvalidator)
self.rotate_y.component('entry').config(width=10)
self.rotate_z = Pmw.EntryField(settingFrame, value='0.0', validate=Pmw.realvalidator)
self.rotate_z.component('entry').config(width=10)
self.rotate_x.place(anchor=NW,x=50,y=70)
self.rotate_y.place(anchor=NW,x=185,y=70)
self.rotate_z.place(anchor=NW,x=275,y=70)
self.scale_x = Pmw.EntryField(settingFrame,label_text='Scale :',labelpos='w',value='1.0', validate=Pmw.realvalidator)
self.scale_x.component('entry').config(width=10)
self.scale_y = Pmw.EntryField(settingFrame,value='1.0', validate=Pmw.realvalidator)
self.scale_y.component('entry').config(width=10)
self.scale_z = Pmw.EntryField(settingFrame, value='1.0', validate=Pmw.realvalidator)
self.scale_z.component('entry').config(width=10)
self.scale_x.place(anchor=NW,x=52,y=100)
self.scale_y.place(anchor=NW,x=185,y=100)
self.scale_z.place(anchor=NW,x=275,y=100)
self.numberOfCopy = Pmw.EntryField(settingFrame,label_text='Number of Copy :',labelpos='w',value='1', validate=Pmw.integervalidator)
self.numberOfCopy.component('entry').config(width=15)
self.numberOfCopy.place(anchor=NW,x=52,y=150)
settingFrame.pack(fill=BOTH,expand=1,padx=7,pady=7)
self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10)
self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT)
mainFrame.pack(fill = 'both', expand = 1,padx=7,pady=7)
def createMenuBar(self):
self.menuBar.destroy()
def onDestroy(self, event):
messenger.send('DW_close')
'''
If you have open any thing, please rewrite here!
'''
pass
###############################
def ok_press(self):
#################################################################
# ok_press(self)
# Callback function
# This function will be called when user click on the "OK" button on the window.
# After collect all data we need for the duplication process,
# this function will send out a message with all data.
# 'DW_duplicating'
# This message will be caught by sceneEditor.
#################################################################
if not self.allEntryValid():
print '---- Duplication Window: Invalid value!!'
return
x = self.move_x.getvalue()
y = self.move_y.getvalue()
z = self.move_z.getvalue()
pos=Vec3(FloatType(x),FloatType(y),FloatType(z))
x = self.rotate_x.getvalue()
y = self.rotate_y.getvalue()
z = self.rotate_z.getvalue()
hpr=Vec3(FloatType(x),FloatType(y),FloatType(z))
x = self.scale_x.getvalue()
y = self.scale_y.getvalue()
z = self.scale_z.getvalue()
scale=Vec3(FloatType(x),FloatType(y),FloatType(z))
num = int(self.numberOfCopy.getvalue())
messenger.send('DW_duplicating',[self.nodePath,pos,hpr,scale,num])
self.quit()
def allEntryValid(self):
#################################################################
# allEntryValid(self)
# This function is used to check all data in the input entries are valid.
#
# For example, none of entries contains blank data.
#
#################################################################
if not self.move_x.valid():
return False
elif not self.move_y.valid():
return False
elif not self.move_z.valid():
return False
elif not self.rotate_x.valid():
return False
elif not self.rotate_y.valid():
return False
elif not self.rotate_z.valid():
return False
elif not self.scale_x.valid():
return False
elif not self.scale_y.valid():
return False
elif not self.scale_z.valid():
return False
elif not self.numberOfCopy.valid():
return False
return True

View file

@ -0,0 +1,493 @@
#################################################################
# lightingPanel.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from direct.tkwidgets.AppShell import AppShell
from seColorEntry import *
from direct.tkwidgets.VectorWidgets import Vector3Entry
from direct.tkwidgets.Slider import Slider
from Tkinter import Frame, Button, Menubutton, Menu
import string, math, types, Pmw, Tkinter
from pandac.PandaModules import *
class lightingPanel(AppShell):
#################################################################
# lightingPanel(AppShell)
# This will create a window to let user
# create any kinds of lighting into the scene
#################################################################
# Override class variables
appname = 'Lighting Panel'
frameWidth = 400
frameHeight = 400
currentLight = None
def __init__(self, lightList, parent = None, **kw):
self.lightList = lightList
self.lightColor = [0.3*255,0.3*255,0.3*255]
self.type = ''
INITOPT = Pmw.INITOPT
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(lightingPanel)
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
menuBar = self.menuBar
self.menuBar.destroy()
# Create a frame to hold all stuff
mainFrame = Frame(interior)
self.listZone = Pmw.Group(mainFrame,tag_pyclass = None)
self.listZone.pack(expand=0, fill=Tkinter.X,padx=3,pady=3)
listFrame = self.listZone.interior()
self.lightEntry = self.createcomponent(
'Lights List', (), None,
Pmw.ComboBox, (listFrame,),label_text='Light :',
labelpos = Tkinter.W, entry_width = 25, selectioncommand = self.selectLight,
scrolledlist_items = self.lightList)
self.lightEntry.pack(side=Tkinter.LEFT)
self.renameButton = self.createcomponent(
'Rename Light', (), None,
Button, (listFrame,),
text = ' Rename ',
command = self.renameLight)
self.renameButton.pack(side=Tkinter.LEFT)
self.addLighZone = Pmw.Group(listFrame,tag_pyclass = None)
self.addLighZone.pack(side=Tkinter.LEFT)
insideFrame = self.addLighZone.interior()
self.lightsButton = Menubutton(insideFrame, text = 'Add light',borderwidth = 3,
activebackground = '#909090')
lightsMenu = Menu(self.lightsButton)
lightsMenu.add_command(label = 'Add Ambient Light',
command = self.addAmbient)
lightsMenu.add_command(label = 'Add Directional Light',
command = self.addDirectional)
lightsMenu.add_command(label = 'Add Point Light',
command = self.addPoint)
lightsMenu.add_command(label = 'Add Spotlight',
command = self.addSpot)
self.lightsButton.pack(expand=0)
self.lightsButton['menu'] = lightsMenu
self.deleteButton = self.createcomponent(
'delete Light', (), None,
Button, (listFrame,),
text = ' Delete ',
command = self.deleteLight)
self.deleteButton.pack(side=Tkinter.LEFT)
self.lightColor = seColorEntry(
mainFrame, text = 'Light Color', value=self.lightColor)
self.lightColor['command'] = self.setLightingColorVec
self.lightColor['resetValue'] = [0.3*255,0.3*255,0.3*255,0]
self.lightColor.pack(fill=Tkinter.X,expand=0)
self.bind(self.lightColor, 'Set light color')
# Notebook pages for light specific controls
self.lightNotebook = Pmw.NoteBook(mainFrame, tabpos = None,
borderwidth = 0)
ambientPage = self.lightNotebook.add('Ambient')
directionalPage = self.lightNotebook.add('Directional')
pointPage = self.lightNotebook.add('Point')
spotPage = self.lightNotebook.add('Spot')
# Put this here so it isn't called right away
self.lightNotebook['raisecommand'] = self.updateLightInfo
# Directional light controls
self.dSpecularColor = seColorEntry(
directionalPage, text = 'Specular Color')
self.dSpecularColor['command'] = self.setSpecularColor
self.dSpecularColor.pack(fill = Tkinter.X, expand = 0)
self.bind(self.dSpecularColor,
'Set directional light specular color')
self.dPosition = Vector3Entry(
directionalPage, text = 'Position')
self.dPosition['command'] = self.setPosition
self.dPosition['resetValue'] = [0,0,0,0]
self.dPosition.pack(fill = Tkinter.X, expand = 0)
self.bind(self.dPosition, 'Set directional light position')
self.dOrientation = Vector3Entry(
directionalPage, text = 'Orientation')
self.dOrientation['command'] = self.setOrientation
self.dOrientation['resetValue'] = [0,0,0,0]
self.dOrientation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.dOrientation, 'Set directional light orientation')
# Point light controls
self.pSpecularColor = seColorEntry(
pointPage, text = 'Specular Color')
self.pSpecularColor['command'] = self.setSpecularColor
self.pSpecularColor.pack(fill = Tkinter.X, expand = 0)
self.bind(self.pSpecularColor,
'Set point light specular color')
self.pPosition = Vector3Entry(
pointPage, text = 'Position')
self.pPosition['command'] = self.setPosition
self.pPosition['resetValue'] = [0,0,0,0]
self.pPosition.pack(fill = Tkinter.X, expand = 0)
self.bind(self.pPosition, 'Set point light position')
self.pConstantAttenuation = Slider(
pointPage,
text = 'Constant Attenuation',
max = 1.0,
resolution = 0.01,
value = 1.0)
self.pConstantAttenuation['command'] = self.setConstantAttenuation
self.pConstantAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.pConstantAttenuation,
'Set point light constant attenuation')
self.pLinearAttenuation = Slider(
pointPage,
text = 'Linear Attenuation',
max = 1.0,
resolution = 0.01,
value = 0.0)
self.pLinearAttenuation['command'] = self.setLinearAttenuation
self.pLinearAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.pLinearAttenuation,
'Set point light linear attenuation')
self.pQuadraticAttenuation = Slider(
pointPage,
text = 'Quadratic Attenuation',
max = 1.0,
resolution = 0.01,
value = 0.0)
self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation
self.pQuadraticAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.pQuadraticAttenuation,
'Set point light quadratic attenuation')
# Spot light controls
self.sSpecularColor = seColorEntry(
spotPage, text = 'Specular Color')
self.sSpecularColor['command'] = self.setSpecularColor
self.sSpecularColor.pack(fill = Tkinter.X, expand = 0)
self.bind(self.sSpecularColor,
'Set spot light specular color')
self.sConstantAttenuation = Slider(
spotPage,
text = 'Constant Attenuation',
max = 1.0,
resolution = 0.01,
value = 1.0)
self.sConstantAttenuation['command'] = self.setConstantAttenuation
self.sConstantAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.sConstantAttenuation,
'Set spot light constant attenuation')
self.sLinearAttenuation = Slider(
spotPage,
text = 'Linear Attenuation',
max = 1.0,
resolution = 0.01,
value = 0.0)
self.sLinearAttenuation['command'] = self.setLinearAttenuation
self.sLinearAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.sLinearAttenuation,
'Set spot light linear attenuation')
self.sQuadraticAttenuation = Slider(
spotPage,
text = 'Quadratic Attenuation',
max = 1.0,
resolution = 0.01,
value = 0.0)
self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation
self.sQuadraticAttenuation.pack(fill = Tkinter.X, expand = 0)
self.bind(self.sQuadraticAttenuation,
'Set spot light quadratic attenuation')
self.sExponent = Slider(
spotPage,
text = 'Exponent',
max = 1.0,
resolution = 0.01,
value = 0.0)
self.sExponent['command'] = self.setExponent
self.sExponent.pack(fill = Tkinter.X, expand = 0)
self.bind(self.sExponent,
'Set spot light exponent')
# MRM: Add frustum controls
self.lightNotebook.setnaturalsize()
self.lightNotebook.pack(expand = 1, fill = Tkinter.BOTH)
mainFrame.pack(expand=1, fill = Tkinter.BOTH)
def onDestroy(self, event):
messenger.send('LP_close')
'''
If you have open any thing, please rewrite here!
'''
pass
def renameLight(self):
#################################################################
# renameLight(self)
# Call Back function
# This function will be called when user push
# the "Rename" button on the panel.
#
# Then, this function will collect data and send out them with a message
# "LP_rename"
# Which will be caught by sceneEditor and pass to dataHolder to
# complete the renaming.
#
#################################################################
oName = self.currentLight
nName = self.lightEntry.get()
messenger.send('LP_rename',[oName,nName])
return
def deleteLight(self):
#################################################################
# deleteLight(self)
# Call Back Function.
# This function will be called when user click on
# the "Delete" button on the panel.
#
# Then, this function will send out a message with current seleted light
# "LP_removeLight"
# Which will be caught by sceneEditor and pass to dataHolder to
# complete the delete process.
#
#################################################################
messenger.send('LP_removeLight',[self.currentLight])
return
def updateList(self, list, node=None):
#################################################################
# updataList(self, list, node = None)
# This function will take a list object which contains names of lights in the scene.
# Also, if user has put node as a parameter,
# this function will automatically select that node as the current working target.
#################################################################
self.lightList = list
self.lightEntry.setlist(list)
if node!=None:
self.lightEntry.selectitem(index=node.getName(), setentry=True )
self.updateDisplay(node)
elif len(list)>0:
self.lightEntry.selectitem(index=0, setentry=True )
self.selectLight(list[0])
else:
self.lightEntry.clear()
return
def selectLight(self, lightName):
#################################################################
# selectLight(self, lightName)
# This function will be called each time when
# user select a light from the list on the panel.
# Then, this function will send out the message,
# 'LP_selectLight' to sceneEditorand get the current light information from dataHolder.
#################################################################
if lightName in self.lightList:
messenger.send('LP_selectLight',[lightName])
return
def updateDisplay(self, lightNode):
#################################################################
# updateDisplay(self, lightNode)
# This function will update the information showing on the panel.
# For example, give a lightNode which is a Point Light.
# This function will switch the page to specify the type.
# Also, new node is the same type with the previous one,
# then this is function won't do the page switching,
# but will call other function to refresh the data to target node.
#################################################################
self.currentLight = lightNode
if self.currentLight != None:
color = lightNode.getLightColor()
self.lightColor.set([255*color.getX(),255*color.getY(),255*color.getZ()])
oldType = self.type
self.type = lightNode.getType()
else:
self.lightColor.set([255*0.3,255*0.3,255*0.3])
oldType = self.type
self.type = 'ambient'
if self.type=='ambient':
self.lightNotebook.selectpage('Ambient')
elif self.type =='directional':
self.lightNotebook.selectpage('Directional')
elif self.type =='point':
self.lightNotebook.selectpage('Point')
elif self.type =='spot':
self.lightNotebook.selectpage('Spot')
if oldType == self.type:
# The same type with previous one, call updateLightInfo to refresh the values.
self.updateLightInfo()
return
def updateLightInfo(self, page=None):
#################################################################
# updateLightInfo(self, page=None)
# This function will refresh the data we user have done any selection.
#################################################################
if self.currentLight != None:
light = self.currentLight.getLight()
if self.type != 'ambient':
specColor = light.getSpecularColor()
if self.type =='directional':
point = self.currentLight.getPosition()
dir = self.currentLight.getOrientation()
self.dSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255])
self.dPosition.set([point.getX(),point.getY(),point.getZ()])
self.dOrientation.set([dir.getX(),dir.getY(),dir.getZ()])
elif self.type =='point':
point = self.currentLight.getPosition()
attenuation = light.getAttenuation()
self.pSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255])
self.pPosition.set([point.getX(),point.getY(),point.getZ()])
self.pConstantAttenuation.set(attenuation.getX())
self.pLinearAttenuation.set(attenuation.getY())
self.pQuadraticAttenuation.set(attenuation.getZ())
elif self.type =='spot':
attenuation = light.getAttenuation()
expo = light.getExponent()
self.sSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255])
self.sConstantAttenuation.set(attenuation.getX())
self.sLinearAttenuation.set(attenuation.getY())
self.sQuadraticAttenuation.set(attenuation.getZ())
self.sExponent.set(expo)
return
def addAmbient(self):
#################################################################
# addAmbient(self)
# This function will send out a message to
# ask dataHolder to create a default ambient light
#################################################################
messenger.send('LP_addLight',['ambient'])
return
def addDirectional(self):
#################################################################
# addDirectional(self)
# This function will send out a message to
# sk dataHolder to create a default Directional light
#################################################################
messenger.send('LP_addLight',['directional'])
return
def addPoint(self):
#################################################################
# addPoint(self)
# This function will send out a message to
# ask dataHolder to create a default Point light
#################################################################
messenger.send('LP_addLight',['point'])
return
def addSpot(self):
#################################################################
# addSpot(self)
# This function will send out a message to
# ask dataHolder to create a default Spot light
#################################################################
messenger.send('LP_addLight',['spot'])
return
def setLightingColorVec(self,color):
#################################################################
# setLightingColorVec(self,color)
# Call Back function. This will be called
# when user try to change the color of light.
#################################################################
if self.currentLight==None:
return
self.currentLight.setColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1))
return
def setSpecularColor(self,color):
#################################################################
# setSpecularColor(self,color)
# Call Back function. This will be called
# when user try to change the Specular color of light.
#################################################################
if self.currentLight==None:
return
self.currentLight.setSpecColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1))
return
def setPosition(self,position):
#################################################################
# setPosition(self,position)
# Call Back function. This will be called
# when user try to change the position of light.
#################################################################
if self.currentLight==None:
return
self.currentLight.setPosition(Point3(position[0],position[1],position[2]))
return
def setOrientation(self, orient):
#################################################################
# setOrientation(self, orient)
# Call Back function. This will be called
# when user try to change the orientation of light.
#################################################################
if self.currentLight==None:
return
self.currentLight.setOrientation(Vec3(orient[0],orient[1],orient[2]))
return
def setConstantAttenuation(self, value):
#################################################################
# setConstantAttenuation(self, value)
# Call Back function. This will be called
# when user try to change the Constant Attenuation of light.
#################################################################
self.currentLight.setConstantAttenuation(value)
return
def setLinearAttenuation(self, value):
#################################################################
# setLinearAttenuation(self, value)
# Call Back function. This will be called
# when user try to change the Linear Attenuation of light.
#################################################################
self.currentLight.setLinearAttenuation(value)
return
def setQuadraticAttenuation(self, value):
#################################################################
# setQuadraticAttenuation(self, value)
# Call Back function. This will be called
# when user try to change the Quadratic Attenuation of light.
#################################################################
self.currentLight.setQuadraticAttenuation(value)
return
def setExponent(self, value):
#################################################################
# setExponent(self, value)
# Call Back function. This will be called
# when user try to change Exponent value of light.
#################################################################
self.currentLight.setExponent(value)
return

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,676 @@
#########################################################################################################################################
# This file implements a Quad View for the level editor
# This feature is not yet enabled in the level editor because picking objects in quad view doesnt work
# I have tried to send the picking function in seSelection.py the correct camera and mouse coordinates but something seems to be wron
# There are two classes in this file..the QuadView and the viewPort...there are four instances of viewport, one for each view in QuadView
#########################################################################################################################################
from direct.showbase.ShowBaseGlobal import *
from direct.interval.IntervalGlobal import *
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *
import math
#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
from pandac.PandaModules import MouseWatcher
class ViewPort:
#########################################################################################################################################
# The ViewPort class has the camera and associated display region set up for actually rendering the four sub-views
# The constructor needs the bounds, window layer, camera, color, projection type, name and scene for the view
#########################################################################################################################################
def __init__(self,X1,X2,Y1,Y2,layer,cam,background=Vec4(0.3,0.3,0.3,1),projection="perspective",type="top",scene=render):
self.VPType=type
self.VP_X1=X1
self.VP_Y1=Y1
self.VP_X2=X2
self.VP_Y2=Y2
self.VP_width=self.VP_X2 - self.VP_X1
self.VP_height=self.VP_Y2 - self.VP_Y1
self.the_viewport=layer.makeDisplayRegion(self.VP_X1, self.VP_X2,self.VP_Y1, self.VP_Y2)
self.the_viewport.setCamera(cam)
self.the_viewport.setClearDepthActive(1)
self.the_viewport.setClearColorActive(1)
self.the_viewport.setClearColor(background)
self.cam=cam
# Set up the cameras to look in the right place.
if(type=="top"):
self.cam.setP(-90)
self.cam.setZ(-40)
elif(type=="left"):
self.cam.setH(-90)
self.cam.setX(10)
elif(type=="front"):
self.cam.setY(-10)
elif(type=="perspective"):
cam.setY(-100)
#cam.setX(10)
#cam.setZ(-10)
#cam.setH(45)
#cam.setP(-45)
#print "aa"
if(projection=="ortho"):
self.lens=OrthographicLens()
self.lens.setAspectRatio((self.VP_X2-self.VP_X1)/(self.VP_Y2-self.VP_Y1))
self.lens.setFilmSize(self.VP_width*200,self.VP_height*200)
#lens.setFilmOffset((self.VP_X2 + self.VP_X1) * 0.5, (self.VP_Y2 + self.VP_Y1) * 0.5)
self.lens.setNearFar(-1000, 1000)
self.cam.node().setLens(self.lens)
self.cam.node().setScene(scene)
elif(projection=="perspective"):
self.lens=base.cam.node().getLens()
self.lens.setAspectRatio((self.VP_X2-self.VP_X1)/(self.VP_Y2-self.VP_Y1))
self.cam.node().setLens(self.lens)
self.cam.node().setScene(scene)
self.the_viewport.setCamera(self.cam)
def resizeX(self,width_increment):
if(self.VPType=="top" or self.VPType=="left"):
self.the_viewport.setDimensions(self.VP_X1,self.VP_X2+width_increment,self.VP_Y1,self.VP_Y2)
elif(self.VPType=="perspective" or self.VPType=="front"):
self.the_viewport.setDimensions(self.VP_X1+width_increment,self.VP_X2,self.VP_Y1,self.VP_Y2)
def resizeY(self,height_increment,direction):
if(self.VPType=="left" or self.type=="perspective"):
self.the_viewport.setDimensions(self.VP_X1,self.VP_X2,self.VP_Y1,self.VP_Y2+height_increment)
else:
self.the_viewport.setDimensions(self.VP_X1,self.VP_X2,self.VP_Y1+height_increment,self.VP_Y2)
def AdjustAspect(self,x,y):
if (y==0):
y=1
self.lens.setAspectRatio(x/y)
self.cam.node().setLens(self.lens)
def resize(self,x,y):
if(self.VPType=="left"):
self.the_viewport.setDimensions(0,x,0,y)
w=abs(x-self.VP_X1)
h=abs(y-self.VP_Y1)
if(h==0):
h=1
self.lens.setAspectRatio(w/h)
self.cam.node().setLens(self.lens)
if(self.VPType=="top"):
self.the_viewport.setDimensions(0,x,y,1)
w=abs(x-self.VP_X1)
h=abs(self.VP_Y2-y)
if(h==0):
h=1
self.lens.setAspectRatio(w/h)
self.cam.node().setLens(self.lens)
if(self.VPType=="front"):
self.the_viewport.setDimensions(x,1,y,1)
w=abs(self.VP_X2-x)
h=abs(self.VP_Y2-y)
if(h==0):
h=1
self.lens.setAspectRatio(w/h)
self.cam.node().setLens(self.lens)
if(self.VPType=="perspective"):
self.the_viewport.setDimensions(x,1,0,y)
w=abs(self.VP_X2-x)
h=abs(y-self.VP_Y1)
if(h==0):
h=1
self.lens.setAspectRatio(w/h)
self.cam.node().setLens(self.lens)
def setScene(self,scene):
self.cam.node().setScene(scene)
def setDR(self,mouseWatcher):
#mouseWatcher.setDisplayRegion(self.the_viewport)
pass
def setCam(self):
#base.cam=self.cam
#base.cam.node().setLens(self.cam.node().getLens())
base.camNode=self.cam.node()
#base.camNode.setLens(self.cam.node().getLens())
#base.camLens=self.cam.node().getLens()
def getCam(self):
return self.cam
class QuadView(DirectObject):
#########################################################################################################################################
# This class sets up four cameras for the scene (ideally we want four instances of render too)
# and then instatiates a ViewPort class for each of them
#
#########################################################################################################################################
def __init__(self):
self.PTracker=1
self.ControlPressed=0
self.AltPressed=0
self.PanConstantX=50
self.PanConstantY=50
self.ZoomConstant=1
self.FrontWidth=100
self.FrontHeight=100
self.TopWidth=100
self.TopHeight=100
self.LeftWidth=100
self.LeftHeight=100
self.MouseButton=0
self.CurrentQuad=4
self.HorizontalAxis=0.0
self.VerticalAxis=0.0
#base.disableMouse()
self.MouseDragging=0
self.currX= 0
self.oldX=self.currX
self.currY= 0
self.oldY=self.currY
self.FrontTexture=1
self.LeftTexture=1
self.PerspectiveTexture=1
self.TopTexture=1
self.FrontWire=0
self.LeftWire=0
self.PerspectiveWire=0
self.TopWire=0
# Keep track of the currently selected window... values are 1-4 for four quadrants of a standard
# Cartesian coordinate system
# These are the orthographic cameras
# They will be restricted to panning and zooming i.e. no rotation
# Top could be flipped to back, left to right and front to back
self.topCam= render.attachNewNode(Camera('topCam'))
self.frontCam = render.attachNewNode(Camera('frontCam'))
self.leftCam= render.attachNewNode(Camera('leftCam'))
# This camera will have a trackball control since its perspective
self.perspectiveCam = render.attachNewNode(Camera('perspectiveCam'))
#self.toplens=OrthographicLens()
#self.leftLens=OrthographicLens()
#self.frontLens=OrthographicLens()
#self.perspectiveLens=base.cam.node().getLens()
# For now all lenses are same as that of base.cam
#self.topCamLens=OrthographicLens()
#self.frontCamLens= base.cam.node().getLens()
#self.leftCamLens= base.cam.node().getLens()
#self.perspectiveCamLens= base.cam.node().getLens()
# Manipulate lenses here if need be
#self.topCamLens.setFilmSize(250)
# Set the Lenses
#self.topCam.node().setLens(self.topCamLens)
#self.frontCam.node().setLens(self.frontCamLens)
#self.leftCam.node().setLens(self.leftCamLens)
#self.perspectiveCam.node().setLens(self.perspectiveCamLens)
#self.badwiz = loader.loadModel('badwizard1')
#self.badwiz.reparentTo(render)
# Create four separate display regions for the quad view.
# These will overlap the main display region
# To stack these overlapping DisplayRegions, we need a new layer. If
# they didn't overlap, we could put them in the same layer.
self.newLayer = base.win.getChannel(0).makeLayer()
self.PerspectiveScene=NodePath('PerspectiveScene')
self.FrontScene=NodePath('FrontScene')
self.TopScene=NodePath('TopScene')
self.LeftScene=NodePath('LeftScene')
self.SceneParent=NodePath('SceneParent')
#self.PerspectiveScene=render.copyTo(self.SceneParent)
#self.FrontScene=render.copyTo(self.SceneParent)
#self.TopScene=render.copyTo(self.SceneParent)
#self.LeftScene=render.copyTo(self.SceneParent)
self.PerspectiveScene=render
self.FrontScene=render
self.TopScene=render
self.LeftScene=render
#self.PerspectiveScene.reparentTo(self.SceneParent)
#self.FrontScene.reparentTo(self.SceneParent)
#self.TopScene.reparentTo(self.SceneParent)
#self.LeftScene.reparentTo(self.SceneParent)
self.Perspective=ViewPort(0.5,1.0,0.0,0.5,self.newLayer,self.perspectiveCam,Vec4(0.75,0.75,0.75,1),"perspective","perspective",self.PerspectiveScene)
self.Top=ViewPort(0.0,0.5,0.5,1.0,self.newLayer,self.topCam,Vec4(0.80,0.80,0.80,1),"ortho","top",self.TopScene)
self.Left=ViewPort(0.0,0.5,0.0,0.5,self.newLayer,self.leftCam,Vec4(0.85,0.85,0.85,1),"ortho","left",self.LeftScene)
self.Front=ViewPort(0.5,1.0,0.5,1.0,self.newLayer,self.frontCam,Vec4(0.85,0.85,0.85,1),"ortho","front",self.FrontScene)
#self.Perspective=None
#self.Top=None
#self.Front=None
#self.Left=None
#self.raycaster = RayCaster( camera )
#self.lastPickPoint = None
#base.useTrackball()
#self.dataRoot = NodePath('dataRoot')
# Cache the node so we do not ask for it every frame
#self.dataRootNode = self.dataRoot.node()
#self.dataUnused = NodePath('dataUnused')
#self.mak=None
#self.mak = self.dataRoot.attachNewNode(MouseAndKeyboard(base.win, 0, 'mak'))
#self.mak.node().setSource(base.win, 0)
self.mouseWatcherNode = MouseWatcher('mouseWatcher')
self.mouseWatcher = base.mak.attachNewNode(self.mouseWatcherNode)
#self.Perspective.setDR(self.mouseWatcherNode)
self.buttonThrower = self.mouseWatcher.attachNewNode(ButtonThrower('buttons'))
#ddr=DisplayRegionContext(self.Perspective.getCam())
#base.setMouseOnNode(self.smiley.node()) # Let Mouse Control Perspective View for now
#base.enableSoftwareMousePointer()
# Message Handlers
self.accept("a",self.setLeft)
self.accept("q",self.setTop)
self.accept("w",self.setFront)
self.accept("s",self.setPerspective)
self.accept("mouse1",self.MouseTell,[1])
self.accept("mouse2",self.MouseTell,[2])
self.accept("mouse3",self.MouseTell,[3])
self.accept("mouse1-up",self.MouseTellUp,[4])
self.accept("mouse2-up",self.MouseTellUp,[5])
self.accept("mouse3-up",self.MouseTellUp,[6])
self.accept("mouse2-scroll",self.resizedr)
self.accept("r",self.resizedr)
self.accept("alt",self.AltHandler)
self.accept("alt-up",self.AltUpHandler)
self.accept("alt-mouse1",self.AltDown)
self.accept("alt-mouse1-up",self.AltUp)
self.accept("control-mouse1",self.CtlDown)
self.accept("control-mouse1-up",self.CtlUp)
# Methods
#def setLastPickPoint( self ):
# mouseX, mouseY = self.mouseWatcherNode.getMouseX(), self.mouseWatcherNode.getMouseY()
# self.lastPickPoint = self.raycaster.pick( mouseX, mouseY )
# print self.lastPickPoint
def AltDown(self):
self.AltPressed=1
def AltUp(self):
self.AltPressed=0
def CtlDown(self):
self.ControlPressed=1
def CtlUp(self):
self.ControlPressed=0
def ToggleWire(self):
if (self.CurrentQuad==1): # Front View
if(self.FrontWire): # Wireframe is On so turn it off
self.FrontScene.setRenderModeWireframe(100)
self.FrontScene.setTwoSided(1)
self.FrontScene.setTextureOff(100)
self.FrontWire=0
else:
self.FrontScene.clearRenderMode()
#self.FrontScene.setTwoSided(not self.backfaceCullingEnabled)
if(self.FrontTexture):
self.FrontScene.clearTexture()
self.FrontWire=1
elif (self.CurrentQuad==2): # Front View
if(self.TopWire): # Wireframe is On so turn it off
self.TopScene.setRenderModeWireframe(100)
self.TopScene.setTwoSided(1)
self.TopScene.setTextureOff(100)
self.TopWire=0
else:
self.TopScene.clearRenderMode()
#self.TopScene.setTwoSided(not self.backfaceCullingEnabled)
if(self.TopTexture):
self.TopScene.clearTexture()
self.TopWire=1
elif (self.CurrentQuad==3): # Front View
if(self.LeftWire): # Wireframe is On so turn it off
self.LeftScene.setRenderModeWireframe(100)
self.LeftScene.setTwoSided(1)
self.LeftScene.setTextureOff(100)
self.LeftWire=0
else:
self.LeftScene.clearRenderMode()
#self.LeftScene.setTwoSided(not self.backfaceCullingEnabled)
if(self.LeftTexture):
self.LeftScene.clearTexture()
self.LeftWire=1
elif (self.CurrentQuad==4): # Front View
if(self.PerspectiveWire): # Wireframe is On so turn it off
self.PerspectiveScene.setRenderModeWireframe(100)
self.PerspectiveScene.setTwoSided(1)
self.PerspectiveScene.setTextureOff(100)
self.PerspectiveWire=0
else:
self.PerspectiveScene.clearRenderMode()
#self.PerspectiveScene.setTwoSided(not self.backfaceCullingEnabled)
if(self.PerspectiveTexture):
self.PerspectiveScene.clearTexture()
self.PerspectiveWire=1
def ToggleTexture(self):
if (self.CurrentQuad==1): # Front View
if(self.FrontTexture): # Texture is on so turn it off
self.FrontScene.setTextureOff(100)
self.FrontTexture=0
else:
self.FrontScene.clearTexture()
self.FrontTexture=1
elif (self.CurrentQuad==2): # Top View
if(self.TopTexture): # Texture is on so turn it off
self.TopScene.setTextureOff(100)
self.TopTexture=0
else:
self.TopScene.clearTexture()
self.TopTexture=1
elif (self.CurrentQuad==3): # Left View
if(self.LeftTexture): # Texture is on so turn it off
self.LeftScene.setTextureOff(100)
self.LeftTexture=0
else:
self.LeftScene.clearTexture()
self.LeftTexture=1
elif (self.CurrentQuad==4): # Perspective View
if(self.PerspectiveTexture): # Texture is on so turn it off
self.PerspectiveScene.setTextureOff(100)
self.PerspectiveTexture=0
else:
self.PerspectiveScene.clearTexture()
self.PerspectiveTexture=1
def reparenter(self):
#self.FrontScene.reparentTo(render)
#self.Front.setScene(render)
#self.Top.setScene(render)
#self.Left.setScene(render)
#self.Perspective.setScene(render)
pass
def unparenter(self):
#self.PerspectiveScene=render.copyTo(render)
#self.FrontScene=render.copyTo(render)
#self.TopScene=render.copyTo(render)
#self.LeftScene=render.copyTo(render)
#self.SceneParent.reparentTo(render)
#self.PerspectiveScene.reparentTo(self.SceneParent)
#self.FrontScene.reparentTo(self.SceneParent)
#self.TopScene.reparentTo(self.SceneParent)
#self.LeftScene.reparentTo(self.SceneParent)
pass
def AltHandler(self):
self.oldX=self.mouseWatcherNode.getMouseX()
if(self.oldX<-1 or self.oldX>1):
return
self.oldY=self.mouseWatcherNode.getMouseY()
if(self.oldY<-1 or self.oldY>1):
return
taskMgr.add(self.DragAction,'DragAction')
def AltUpHandler(self):
taskMgr.remove('DragAction')
def gridtoggle(self):
#grid=DirectGrid()
#grid.enable()
pass
def resizedr(self,x,y):
#print "X: " + str(x) + " Y: " + str(y)
x=(x+1)/2.0
y=(y+1)/2.0
self.Perspective.resize(x,y)
self.Top.resize(x,y)
self.Front.resize(x,y)
self.Left.resize(x,y)
def setAppropriateViewPort(self,x,y):
#print "SET APPROPRIATE:" + str(x) + " " + str(y)
if(x<self.VerticalAxis):
if(y<self.HorizontalAxis):
self.setLeft()
else:
self.setTop()
else:
if(y<self.HorizontalAxis):
self.setPerspective()
else:
self.setFront()
def MouseTell(self,buttonCode):
self.MouseButton=buttonCode
self.setAppropriateViewPort(self.mouseWatcherNode.getMouseX(),self.mouseWatcherNode.getMouseY())
x=base.mouseWatcherNode.getMouseX()
y=base.mouseWatcherNode.getMouseY()
#Perspective and Front
if(self.CurrentQuad==4 or self.CurrentQuad==1):
x1=abs(x-self.VerticalAxis)
w1=abs(1-self.VerticalAxis)
x2=x1*2.0/w1
ansX=-1+x2
#Left and top
if(self.CurrentQuad==2 or self.CurrentQuad==3):
x1=abs(x-(-1.0))
w1=abs(self.VerticalAxis-(-1.0))
x2=x1*2.0/w1
ansX=-1.0+x2
#Left and Perspective
if(self.CurrentQuad==4 or self.CurrentQuad==3):
y1=abs(y-(-1.0))
h1=abs(self.HorizontalAxis-(-1.0))
y2=y1*2.0/h1
ansY=-1.0+y2
#Front and top
if(self.CurrentQuad==1 or self.CurrentQuad==2):
y1=abs(y-self.HorizontalAxis)
h1=abs(1.0-self.HorizontalAxis)
y2=y1*2.0/h1
ansY=-1.0+y2
self.xy=[ansX,ansY]
print "Sent X:%f Sent Y:%f"%(ansX,ansY)
#SEditor.iRay.pick(render,self.xy)
SEditor.manipulationControl.manipulationStop(self.xy)
#print "MouseX " + str(base.mouseWatcherNode.getMouseX()) + "MouseY " + str(base.mouseWatcherNode.getMouseY()) + "\n"
#print "MouseX " + str(self.mouseWatcherNode.getMouseX()) + "MouseY " + str(self.mouseWatcherNode.getMouseY()) + "\n"
base.mouseWatcherNode=self.mouseWatcherNode
self.oldX=self.mouseWatcherNode.getMouseX()
if(self.oldX<-1 or self.oldX>1):
return
self.oldY=self.mouseWatcherNode.getMouseY()
if(self.oldY<-1 or self.oldY>1):
return
self.Mouse_Dragging=1
taskMgr.add(self.DragAction,'DragAction')
def MouseTellUp(self,buttoncode):
#self.MouseButton=0
self.PanConstantX= 50
self.PanConstantY= 50
self.ZoomConstant=1
taskMgr.remove('DragAction')
self.Mouse_Draggin=0
#print "Mouse Up"
def Max_Style_Mouse_View(self,buttoncode):
pass
def ChangeBaseDR(self):
dr=base.win.getDisplayRegion(0)
if(self.CurrentQuad==1): #Front
dr.setDimensions(0.5,1,0.5,1)
elif(self.CurrentQuad==2): #Top
dr.setDimensions(0,0.5,0.5,1)
elif(self.CurrentQuad==3): #Left
dr.setDimensions(0,0.5,0,0.5)
elif(self.CurrentQuad==4): #Perspective
dr.setDimensions(0.5,1,0,0.5)
def setLeft(self):
print "LEFT"
self.CurrentQuad=3
self.ChangeBaseDR()
self.Left.setCam()
#self.Left.setDR(self.mouseWatcherNode)
def setTop(self):
print "TOP"
self.CurrentQuad=2
self.ChangeBaseDR()
self.Top.setCam()
#self.Top.setDR(self.mouseWatcherNode)
def setPerspective(self):
print "PERSPECTIVE"
self.CurrentQuad=4
self.ChangeBaseDR()
self.Perspective.setCam()
#self.Perspective.setDR(self.mouseWatcherNode)
def setFront(self):
print "FRONT"
self.CurrentQuad=1
self.ChangeBaseDR()
self.Front.setCam()
#self.Front.setDR(self.mouseWatcherNode)
def DragAction(self,task):
#if(self.MouseDragging==1):
self.currX= self.mouseWatcherNode.getMouseX()
if(self.currX<-1 or self.currX>1):
return
self.currY= self.mouseWatcherNode.getMouseY()
if(self.currY<-1 or self.currY>1):
return
self.diffX=self.currX-self.oldX
self.diffY=self.currY-self.oldY
if(self.ControlPressed): # Change Size of the ViewPorts
#if(base.getControl()):
self.VerticalAxis=self.currX
self.HorizontalAxis=self.currY
if(self.HorizontalAxis<-1 or self.HorizontalAxis>1 or self.VerticalAxis<-1 or self.VerticalAxis>1):
return
self.resizedr(self.VerticalAxis,self.HorizontalAxis)
#if(self.AltPressed): # View Camera Transforms -> Maya style
elif(1):
#print "ALTPRESSED"
if(self.PanConstantX<4096):
self.PanConstantX= self.PanConstantX * 2
self.PanConstantY= self.PanConstantY * 2
self.ZoomConstant= self.ZoomConstant + 50
if(self.MouseButton==1): # TrackBall rotation only for Perspective View
if(self.CurrentQuad==4):
pass
elif(self.MouseButton==2): # Do Panning
if(self.CurrentQuad==1): # Y and Z values change meanings for different cameras
self.MoveCamera(-self.diffX*self.PanConstantX,0,-self.diffY*self.PanConstantY,self.CurrentQuad)
elif(self.CurrentQuad==2):
self.MoveCamera(-self.diffX*self.PanConstantX,-self.diffY*self.PanConstantY,0,self.CurrentQuad)
elif(self.CurrentQuad==3):
self.MoveCamera(0,self.diffX*self.PanConstantX,-self.diffY*self.PanConstantY,self.CurrentQuad)
elif(self.CurrentQuad==4):
pass
elif(self.MouseButton==3): # Do Zoom
if(self.CurrentQuad==1): # Y and Z values change meanings for different cameras
#lens = OrthographicLens()
#lens.setFilmSize(l,self.VP_height*200)
#lens.setFilmOffset((self.VP_X2 + self.VP_X1) * 0.5, (self.VP_Y2 + self.VP_Y1) * 0.5)
#lens.setNearFar(-1000, 1000)
self.FrontWidth= self.FrontWidth + self.diffX
self.FrontHeight= self.FrontHeight + self.diffX
self.FrontWidth= self.FrontWidth + self.diffY
self.FrontHeight= self.FrontHeight + self.diffY
if(self.FrontWidth<=0):
Frontwidth=1
if(self.FrontHeight<=0):
FrontHeight=1
self.frontCam.node().getLens().setFilmSize(self.FrontWidth,self.FrontHeight)
self.resizedr(self.VerticalAxis,self.HorizontalAxis)
elif(self.CurrentQuad==2):
self.TopWidth= self.TopWidth + self.diffX
self.TopHeight= self.TopHeight + self.diffX
self.TopWidth= self.TopWidth + self.diffY
self.TopHeight= self.TopHeight + self.diffY
self.topCam.node().getLens().setFilmSize(self.TopWidth,self.TopHeight)
self.resizedr(self.VerticalAxis,self.HorizontalAxis)
elif(self.CurrentQuad==3):
self.LeftWidth= self.LeftWidth + self.diffX
self.LeftHeight= self.LeftHeight + self.diffX
self.LeftWidth= self.LeftWidth + self.diffY
self.LeftHeight= self.LeftHeight + self.diffY
self.leftCam.node().getLens().setFilmSize(self.LeftWidth,self.LeftHeight)
self.resizedr(self.VerticalAxis,self.HorizontalAxis)
elif(self.CurrentQuad==4):
pass
else:
pass
self.oldX=self.currX
self.oldY=self.currY
return Task.cont
def MoveCamera(self,X_amt,Y_amt,Z_amt,quad):
if(quad==1):
self.frontCam.setPos(self.frontCam.getX()+X_amt,self.frontCam.getY()+Y_amt,self.frontCam.getZ()+Z_amt)
elif(quad==2):
self.topCam.setPos(self.topCam.getX()+X_amt,self.topCam.getY()+Y_amt,self.topCam.getZ()+Z_amt)
elif(quad==3):
self.leftCam.setPos(self.leftCam.getX()+X_amt,self.leftCam.getY()+Y_amt,self.leftCam.getZ()+Z_amt)
elif(quad==4):
self.perspectiveCam.setPos(self.perspectiveCam.getX()+X_amt,self.perspectiveCam.getY()+Y_amt,self.perspectiveCam.getZ()+Z_amt)
#View=QuadView()
#run()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,608 @@
#################################################################
# collisionWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
from tkSimpleDialog import askfloat
import string
import math
import types
from direct.task import Task
FRAMES = 0
SECONDS = 1
class AnimPanel(AppShell):
#################################################################
# This class will generate a animation panel for an actor
# which user assigned. Inside this panel, instead of using actorInterval
# or just simply calling the play function in Actor, we create a task to
# set animation frame by frame using setPose.
#################################################################
# Override class variables
appname = 'Anim Panel'
frameWidth = 575
frameHeight = 250
usecommandarea = 0
usestatusarea = 0
index = 0
dragMode = False
rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0']
def __init__(self, aNode = None, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'AnimPanel '+ aNode.getName()
self.appname = self.id
optiondefs = (
('title', self.appname, None),
('actor', aNode, None),
('animList', [], None),
)
self.defineoptions(kw, optiondefs)
self.frameHeight = 300
self.id = 'AnimPanel '+ aNode.getName()
self.nodeName = aNode.getName()
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(AnimPanel)
self.currTime = 0.0 # Initialize the start time
self.animName = None
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
menuBar = self.menuBar
menuBar.addmenu('Anim', 'Anim Panel Operations')
# Reset all actor controls
menuBar.addmenuitem('File', 'command',
'Load Animation',
label = 'Load Animation',
command = self.loadAnimation)
menuBar.addmenuitem('Anim', 'command',
'Set actor controls to t = 0.0',
label = 'Jump all to zero',
command = self.resetAllToZero)
menuBar.addmenuitem('Anim', 'command',
'Set Actor controls to end time',
label = 'Jump all to end time',
command = self.resetAllToEnd)
menuBar.addmenuitem('Anim', 'separator')
menuBar.addmenuitem('Anim', 'command',
'Play Current Animation',
label = 'Play',
command = self.play)
menuBar.addmenuitem('Anim', 'command',
'Stop Current Animation',
label = 'stop',
command = self.stop)
# Create a frame to hold all the actor controls
actorFrame = Frame(interior)
name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 16),
relief = SUNKEN, borderwidth=3)
name_label.place(x=5,y=5,anchor=NW)
Label(actorFrame, text= "Animation:", font=('MSSansSerif', 12)).place(x=140,y=5,anchor=NW)
Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 12)).place(x=140,y=35,anchor=NW)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntry = self.createcomponent(
'AnimationMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setAnimation,
scrolledlist_items = self['animList'])
self.AnimEntry.place(x=240,y=10,anchor=NW)
self.playRateEntry = self.createcomponent(
'playRateMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate,
scrolledlist_items = self.rateList)
self.playRateEntry.place(x=240,y=40,anchor=NW)
self.playRateEntry.selectitem('1.0')
### Loop checkbox
Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=05,anchor=NW)
self.loopVar = IntVar()
self.loopVar.set(0)
self.loopButton = self.createcomponent(
'loopButton', (), None,
Checkbutton, (actorFrame,),
variable = self.loopVar)
self.loopButton.place(x=470,y=7,anchor=NW)
### Display Frames/Seconds
Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 11)).place(x=5,y=75,anchor=NW)
self.unitsVar = IntVar()
self.unitsVar.set(FRAMES)
self.displayButton = self.createcomponent(
'displayButton', (), None,
Checkbutton, (actorFrame,),
command = self.updateDisplay,
variable = self.unitsVar)
self.displayButton.place(x=120,y=77,anchor=NW)
## scale control
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
self.minLabel = self.createcomponent(
'minLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0)
self.minLabel.pack(side = LEFT)
self.frameControl = self.createcomponent(
'scale', (), None,
Scale, (frameFrame,),
from_ = 0, to = 24, resolution = 1.0,
command = self.goTo, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.frameControl.pack(side = LEFT, expand = 1)
self.frameControl.bind('<Button-1>', self.onPress)
self.frameControl.bind('<ButtonRelease-1>', self.onRelease)
self.maxLabel = self.createcomponent(
'maxLabel', (), 'sLabel',
Label, (frameFrame,),
text = 24)
self.maxLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
## button contorl
ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5)
self.toStartButton = self.createcomponent(
'toStart', (), None,
Button, (ButtomFrame,),
text = '<<',
width = 8,
command = self.resetAllToZero)
self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
self.playButton = self.createcomponent(
'playButton', (), None,
Button, (ButtomFrame,),
text = 'Play', width = 8,
command = self.play)
self.playButton.pack(side = LEFT, expand = 1, fill = X)
self.stopButton = self.createcomponent(
'stopButton', (), None,
Button, (ButtomFrame,),
text = 'Stop', width = 8, state=DISABLED,
command = self.stop)
self.stopButton.pack(side = LEFT, expand = 1, fill = X)
self.toEndButton = self.createcomponent(
'toEnd', (), None,
Button, (ButtomFrame,),
text = '>>',
width = 8,
command = self.resetAllToEnd)
self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
ButtomFrame.place(anchor=NW,x=5,y=165)
self.removeButton = self.createcomponent(
'Remove Animation', (), None,
Button, (actorFrame,),
text = 'Remove This Animation', width = 20,
command = self.removeAnim)
self.removeButton.place(anchor=NW,x=5,y=220)
self.loadButton = self.createcomponent(
'Load Animation', (), None,
Button, (actorFrame,),
text = 'Load New Animation', width = 20,
command = self.loadAnimation)
self.loadButton.place(anchor=NW,x=180,y=220)
# Now pack the actor frame
actorFrame.pack(expand = 1, fill = BOTH)
def updateList(self):
#################################################################
# updateList(self)
# This function will update the list of animations that the Actor
# currently has into the combo box widget.
#################################################################
self.ignore('DataH_loadFinish'+self.nodeName)
del self.loaderWindow
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntry.setlist(animL)
def removeAnim(self):
#################################################################
# removeAnim(self)
# This function will stop the animation and get the name of animation
# which user wish to remove from the panel. Then, it will send out
# a message to dataHolder to remove the target animation.
# And in the same time, it will start waiting a return message to
# make sure that target animation has been removed.
#################################################################
name = self.AnimEntry.get()
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
self.stop()
self.accept('DataH_removeAnimFinish'+self.nodeName,self.afterRemove)
messenger.send('AW_removeAnim',[self['actor'],name])
return
def afterRemove(self):
#################################################################
# afterRemove(self)
# This function will be called once panel has received the return
# message from dataHolder. This function will call setList to
# reset the list of Animations
#################################################################
self.ignore('DataH_removeAnimFinish'+self.nodeName)
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntry.setlist(animL)
print '-----',animL
return
def loadAnimation(self):
#################################################################
# loadAnimation(self)
# This function will open a dialog window to require user to input
# the animation he wants to load in for this actor.
#################################################################
self.loaderWindow = LoadAnimPanel(aNode=self['actor'])
self.accept('DataH_loadFinish'+self.nodeName,self.updateList)
return
def play(self):
#################################################################
# play(self)
# This function will be called when user click on the "play" button.
# First, this function will initialize all parameter that the actual
# play task need to run and add the play task into the taskMgr.
#################################################################
self.animName = self.AnimEntry.get()
if self.animName in self['animList']:
animName = self.AnimEntry.get()
self.playButton.config(state=DISABLED)
self.lastT = globalClock.getFrameTime()
taskMgr.add(self.playTask, self.id + '_UpdateTask')
self.stopButton.config(state=NORMAL)
else:
print '----Illegal Animaion name!!', self.animName
return
def playTask(self, task):
#################################################################
# playTask(self, task)
# This task will record time by each frame
# In fact it is just a clock keeper.
# If the current frame time over the max long of the animation,
# it will reset the timer.
# Anyway, this function will call gotoT by each frame.
#################################################################
fLoop = self.loopVar.get()
currT = globalClock.getFrameTime()
deltaT = currT - self.lastT
self.lastT = currT
if self.dragMode:
return Task.cont
self.currTime = self.currTime + deltaT
if (self.currTime > self.maxSeconds):
if fLoop:
self.currTime = self.currTime%self.duration
self.gotoT(self.currTime)
else:
self.currTime = 0.0
self.gotoT(0.0)
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return Task.done
else:
self.gotoT(self.currTime)
return Task.cont
def stop(self):
#################################################################
# stop(self)
# This function will remove the play task from taskMgr when user
# click on the "Stop" button
#################################################################
taskMgr.remove(self.id + '_UpdateTask')
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return
def setAnimation(self, animation):
#################################################################
# setAnimation(self, animation)
# This function will be called each time when user change
# the current animation. Most important thing this function do is
# to recalculate all variables to fit the selected animation
#################################################################
self.animName = self.AnimEntry.get()
playRate = '%0.1f' % self['actor'].getPlayRate(self.animName)
if playRate not in self.rateList:
def strCmp(a, b):
return cmp(eval(a), eval(b))
self.rateList.append(playRate)
self.rateList.sort(strCmp)
self.playRateEntry.reset(self.rateList)
self.playRateEntry.selectitem(playRate)
self.currTime = 0.0
self.frameControl.set(0)
self.updateDisplay()
return
def setPlayRate(self,rate):
#################################################################
# setPlayRate(self, rate)
# This function will be called each time when user changes the current play rate.
#################################################################
self.animName = self.AnimEntry.get()
if self.animName in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animName)
self.updateDisplay()
return
def updateDisplay(self):
#################################################################
# updateDisplay(self)
# This function will be called whenever something has been changed
# on the panel. In here we will re-new all widgets on the panel to
# correct value.
#################################################################
self.fps = self['actor'].getFrameRate(self.animName)
self.duration = self['actor'].getDuration(self.animName)
self.maxFrame = self['actor'].getNumFrames(self.animName) - 1
self.maxSeconds = self.duration
if self.unitsVar.get() == FRAMES:
fromFrame = 0
toFrame = self.maxFrame
self.minLabel['text'] = fromFrame
self.maxLabel['text'] = toFrame
self.frameControl.configure(from_ = fromFrame,
to = toFrame,
resolution = 1.0)
else:
self.minLabel['text'] = '0.0'
self.maxLabel['text'] = "%.2f" % self.duration
self.frameControl.configure(from_ = 0.0,
to = self.duration,
resolution = 0.01)
def gotoT(self,time):
#################################################################
# gotoT(self, time)
# calculate the right parameter which will be send to set Frame
# Control slider, which is the real place we play the animation.
#################################################################
if self.unitsVar.get() == FRAMES:
self.frameControl.set(time * self.fps)
else:
self.frameControl.set(time)
return
def goTo(self,frame):
#################################################################
# goto(self, frame)
# Call back function for the frame control slider.
# This function will set the animation by the value on the slider.
#
# This function is the real function we "play" the animation.
#
#################################################################
if self.animName in self['animList']:
# Convert scale value to float
frame = string.atof(frame)
# Now convert t to seconds for offset calculations
if self.unitsVar.get() == FRAMES:
frame = frame / self.fps
if self.dragMode:
# If user is clicking on the slider and is draging the bar, reset the global timer.
self.currTime = frame
self['actor'].pose(self.animName,
min(self.maxFrame, int(frame * self.fps)))
return
def onRelease(self,frame):
#################################################################
# onRelease(self, frame)
# disable the dragMode when user releases the bar on the slider.
#################################################################
self.dragMode = False
return
def onPress(self,frame):
#################################################################
# onPress(self, frame)
# enable the dragMode when user press the bar on the slider.
#################################################################
self.dragMode = True
return
def resetAllToZero(self):
#################################################################
# resetAllToZero(self)
# reset the global timer to zero and also move the slider to zero.
# This will also reset the actor to the zero frame of current animation
#################################################################
self.currTime = 0.0
self.gotoT(0)
return
def resetAllToEnd(self):
#################################################################
# resetAllToEnd(self)
# set the global timer to the end of current animation and
# also move the slider to the end.
#################################################################
self.currTime = self.maxSeconds
self.gotoT(self.duration)
return
def onDestroy(self, event):
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
taskMgr.remove(self.id + '_UpdateTask')
self.ignore('DataH_loadFinish')
messenger.send('AW_close',[self.nodeName])
'''
If you have open any thing, please rewrite here!
'''
pass
class LoadAnimPanel(AppShell):
#################################################################
# LoadAnimPanel(AppShell)
# This class will open a dialog to ask user to input names and
# file paths of animations
#################################################################
# Override class variables
appname = 'Load Animation'
frameWidth = 575
frameHeight = 200
usecommandarea = 0
usestatusarea = 0
index = 0
## Anim name : File Path
def __init__(self, aNode = None, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'Load Animation '+ aNode.getName()
self.appname = self.id
self.animDic = {}
self.animList = []
optiondefs = (
('title', self.appname, None),
)
self.defineoptions(kw, optiondefs)
self.frameHeight = 300
self.nodeName = aNode.getName()
self.Actor = aNode
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(LoadAnimPanel)
def createInterface(self):
self.menuBar.destroy()
interior = self.interior()
mainFrame = Frame(interior)
self.inputZone = Pmw.Group(mainFrame, tag_text='File Setting')
self.inputZone.pack(fill='both',expand=1)
settingFrame = self.inputZone.interior()
Label(settingFrame,text='Anim Name').place(anchor=NW,x=60,y=5)
Label(settingFrame,text='File Path').place(anchor=NW,x=205,y=5)
self.AnimName_1 = self.createcomponent(
'Anim Name List', (), None,
Pmw.ComboBox, (settingFrame,),label_text='Anim :',
labelpos = W, entry_width = 10, selectioncommand = self.selectAnim,
scrolledlist_items = self.animList)
self.AnimFile_1 = Pmw.EntryField(settingFrame,value='')
self.AnimFile_1.component('entry').config(width=20)
self.AnimName_1.place(anchor=NW,x=10,y=25)
self.AnimFile_1.place(anchor=NW,x=140,y=25)
self.Browse_1 = self.createcomponent(
'File Browser1', (), None,
Button, (mainFrame,),
text = 'Browse...',
command = self.Browse_1)
self.Browse_1.place(anchor=NW,x=270,y=38)
self.addIntoButton = self.createcomponent(
'Load Add', (), None,
Button, (mainFrame,),
text = 'Add to Load',
command = self.addIntoList)
self.addIntoButton.place(anchor=NW,x=345,y=38)
att_label = Label(mainFrame, font=('MSSansSerif', 10),
text= "Attention! Animations won't be loaded in before you press the 'OK' button below!")
att_label.place(anchor=NW,x=10,y=80)
self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10)
self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT)
mainFrame.pack(expand = 1, fill = BOTH)
def onDestroy(self, event):
messenger.send('AWL_close',[self.nodeName])
'''
If you have open any thing, please rewrite here!
'''
pass
def selectAnim(self,name):
#################################################################
# selectAnim(self, name)
# This function will be called if user select an animation on the list.
#################################################################
if name in self.animDic:
self.AnimFile_1.setvalue = self.animDic[name]
return
def Browse_1(self):
#################################################################
# Browse_1(self)
# when the browse button pused, this function will be called.
# Do nothing but open a file dialog for user to set the path to target file
# Then, set the path back to the entry on the panel.
#################################################################
AnimFilename = askopenfilename(
defaultextension = '.egg',
filetypes = (('Egg Files', '*.egg'),
('Bam Files', '*.bam'),
('All files', '*')),
initialdir = '.',
title = 'File Path for Anim 1',
parent = self.parent)
if AnimFilename:
self.AnimFile_1.setvalue(AnimFilename)
return
def addIntoList(self):
#################################################################
# addIntoList(self)
# This function will be called each time when user click on the
# "Add to Load" button. This function will read the current data
# on the panel into a dictionary. then reset the list of the animation
# name list on this panel.(not the one in the animation panel...)
#
# This function won't load any animation....
#
#################################################################
name = self.AnimName_1.get()
self.animDic[name] = Filename.fromOsSpecific(self.AnimFile_1.getvalue()).getFullpath()
if name in self.animList:
pass
else:
self.animList.append(name)
self.AnimName_1.setlist(self.animList)
print self.animDic
return
def ok_press(self):
#################################################################
# ok_press(Self)
# This functiion will be called when user click on the "OK"
# button. This function will send a message along with the animation
# file we wish to load for this actor.
# Then, it will close the panel itself.
#################################################################
messenger.send('AW_AnimationLoad',[self.Actor,self.animDic])
#print self.animDic
self.quit()
return

View file

@ -0,0 +1,666 @@
#################################################################
# collisionWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
from tkSimpleDialog import askfloat
import string
import math
import types
from direct.task import Task
FRAMES = 0
SECONDS = 1
#####################################################################################
# BlendAnimPanel(AppShell)
# This Panel will allow user to blend tow animations
# that have already been loaded for this actor.
# user can play and manipulate this blended animation
# just like in the animation panel. And, they can save this blended animation.
#####################################################################################
class BlendAnimPanel(AppShell):
# Override class variables
appname = 'Blend Anim Panel'
frameWidth = 575
frameHeight = 450
usecommandarea = 0
usestatusarea = 0
index = 0
dragMode = False
blendRatio = 0
rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0']
enableBlend = False
currentBlendName = None
def __init__(self, aNode = None, blendDict={}, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'BlendAnimPanel '+ aNode.getName()
self.appname = self.id
self.actorNode = aNode
self.blendDict = blendDict.copy()
if len(blendDict)>0:
self.blendList = blendDict.keys()
else:
self.blendList = []
optiondefs = (
('title', self.appname, None),
('actor', aNode, None),
('animList', [], None),
('blendAnimList', self.blendList, None),
)
self.defineoptions(kw, optiondefs)
self.id = 'Blend AnimPanel '+ aNode.getName()
self.nodeName = aNode.getName()
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(BlendAnimPanel)
self.currTime = 0.0
self.animNameA = None
self.animNameB = None
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
self.menuBar.destroy()
# show the actor's name
actorFrame = Frame(interior)
name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 14),
relief = SUNKEN, borderwidth=3)
name_label.pack(side = TOP, expand = False)
actorFrame.pack(side = TOP, expand = False, fill = X)
# Create a frame to show is there any ore-blended animation and save, edit, rename button.
group = Pmw.Group(interior, tag_pyclass=None)
actorFrame = group.interior()
group.pack(side = TOP, expand = False, fill = X)
Label(actorFrame, text= "Blended:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.blendAnimEntry = self.createcomponent(
'Blended Animation', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setBlendAnim,
scrolledlist_items = self['blendAnimList'])
self.blendAnimEntry.pack(side=LEFT)
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT)
button = Button(actorFrame, text="Save", font=('MSSansSerif', 10),width = 12,
command = self.saveButtonPushed).pack(side=LEFT)
button = Button(actorFrame, text="Remove", font=('MSSansSerif', 10),width = 12,
command = self.removeButtonPushed).pack(side=LEFT)
button = Button(actorFrame, text="Rename", font=('MSSansSerif', 10),width = 12,
command = self.renameButtonPushed).pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
# Create a frame to hold all the animation setting
group = Pmw.Group(interior, tag_pyclass=None)
actorFrame = group.interior()
group.pack(side = TOP, expand = False, fill = X)
Label(actorFrame, text= "Animation A:", font=('MSSansSerif', 10)).pack(side=LEFT)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntryA = self.createcomponent(
'AnimationMenuA', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, entry_state = DISABLED,
selectioncommand = lambda name, a = 'a' : self.setAnimation(name, AB=a),
scrolledlist_items = self['animList'])
self.AnimEntryA.pack(side=LEFT)
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,)
Label(actorFrame, text= "Animation B:", font=('MSSansSerif', 10)).pack(side=LEFT)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntryB = self.createcomponent(
'AnimationMenuB', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, entry_state = DISABLED,
selectioncommand = lambda name, a = 'b' : self.setAnimation(name, AB=a),
scrolledlist_items = self['animList'])
self.AnimEntryB.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
### Blend Enable checkbox
actorFrame = Frame(interior, relief = SUNKEN, bd = 1)
Label(actorFrame, text= "Enable Blending:", font=('MSSansSerif', 10)).pack(side=LEFT,)
self.blendVar = IntVar()
self.blendVar.set(0)
self.blendButton = self.createcomponent(
'blendButton', (), None,
Checkbutton, (actorFrame,),
variable = self.blendVar,
command = self.toggleBlend)
self.blendButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
## Ratio control
actorFrame = Frame(interior)
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
minRatioLabel = self.createcomponent(
'minRatioLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0.00)
minRatioLabel.pack(side = LEFT)
self.ratioControl = self.createcomponent(
'ratio', (), None,
Scale, (frameFrame,),
from_ = 0.0, to = 1.0, resolution = 0.01,
command = self.setRatio, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.ratioControl.pack(side = LEFT, expand = 1)
self.ratioControl.set(1.0)
self.maxRatioLabel = self.createcomponent(
'maxRatioLabel', (), 'sLabel',
Label, (frameFrame,),
text = 1.00)
self.maxRatioLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
actorFrame.pack(side = TOP, expand = True, fill = X)
###################################################################################
###################################################################################
actorFrame = Frame(interior)
Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.playRateEntry = self.createcomponent(
'playRateMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate,
scrolledlist_items = self.rateList)
self.playRateEntry.pack(side=LEFT)
self.playRateEntry.selectitem('1.0')
### Loop checkbox
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,)
Label(actorFrame, text= "Loop:", font=('MSSansSerif', 10)).pack(side=LEFT,)
self.loopVar = IntVar()
self.loopVar.set(0)
self.loopButton = self.createcomponent(
'loopButton', (), None,
Checkbutton, (actorFrame,),
variable = self.loopVar)
self.loopButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = True, fill = X)
### Display Frames/Seconds
actorFrame = Frame(interior)
Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.unitsVar = IntVar()
self.unitsVar.set(FRAMES)
self.displayButton = self.createcomponent(
'displayButton', (), None,
Checkbutton, (actorFrame,),
command = self.updateDisplay,
variable = self.unitsVar)
self.displayButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = True, fill = X)
## scale control
actorFrame = Frame(interior)
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
self.minLabel = self.createcomponent(
'minLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0)
self.minLabel.pack(side = LEFT)
self.frameControl = self.createcomponent(
'scale', (), None,
Scale, (frameFrame,),
from_ = 0, to = 24, resolution = 1.0,
command = self.goTo, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.frameControl.pack(side = LEFT, expand = 1)
self.frameControl.bind('<Button-1>', self.onPress)
self.frameControl.bind('<ButtonRelease-1>', self.onRelease)
self.maxLabel = self.createcomponent(
'maxLabel', (), 'sLabel',
Label, (frameFrame,),
text = 24)
self.maxLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
actorFrame.pack(side = TOP, expand = True, fill = X)
## button contorl
actorFrame = Frame(interior)
ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5)
self.toStartButton = self.createcomponent(
'toStart', (), None,
Button, (ButtomFrame,),
text = '<<',
width = 8,
command = self.resetAllToZero)
self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
self.playButton = self.createcomponent(
'playButton', (), None,
Button, (ButtomFrame,),
text = 'Play', width = 8,
command = self.play)
self.playButton.pack(side = LEFT, expand = 1, fill = X)
self.stopButton = self.createcomponent(
'stopButton', (), None,
Button, (ButtomFrame,),
text = 'Stop', width = 8, state=DISABLED,
command = self.stop)
self.stopButton.pack(side = LEFT, expand = 1, fill = X)
self.toEndButton = self.createcomponent(
'toEnd', (), None,
Button, (ButtomFrame,),
text = '>>',
width = 8,
command = self.resetAllToEnd)
self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
ButtomFrame.pack(side = TOP, expand = True, fill = X)
actorFrame.pack(expand = 1, fill = BOTH)
def updateList(self):
#################################################################
# updateList(self)
# This will reset the list of all animations that this actor has
# to the animation entry A and B.
#################################################################
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntryA.setlist(animL)
self.AnimEntryB.setlist(animL)
def play(self):
#################################################################
# play(self)
# It works pretty much like what we have in the Animation Panel.
# The only different now is that we set two "pose" here.
# When you do the blending animation by setPose, you don't have
# to set them simultaneously.
#################################################################
self.animNameA = self.AnimEntryA.get()
self.animNameB = self.AnimEntryB.get()
if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
self.playButton.config(state=DISABLED)
self.lastT = globalClock.getFrameTime()
taskMgr.add(self.playTask, self.id + '_UpdateTask')
self.stopButton.config(state=NORMAL)
else:
print '----Illegal Animaion name!!', self.animNameA + ', '+ self.animNameB
return
def playTask(self, task):
#################################################################
# playTask(self, task)
# see play(self)
#################################################################
fLoop = self.loopVar.get()
currT = globalClock.getFrameTime()
deltaT = currT - self.lastT
self.lastT = currT
if self.dragMode:
return Task.cont
self.currTime = self.currTime + deltaT
if (self.currTime > self.maxSeconds):
if fLoop:
self.currTime = self.currTime%self.duration
self.gotoT(self.currTime)
else:
self.currTime = 0.0
self.gotoT(0.0)
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return Task.done
else:
self.gotoT(self.currTime)
return Task.cont
def stop(self):
#################################################################
# stop(self)
# see play(self)
#################################################################
taskMgr.remove(self.id + '_UpdateTask')
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return
def setAnimation(self, animation, AB = 'a'):
#################################################################
# setAnimation(self, animation, AB = 'a')
# see play(self)
#################################################################
print 'OK!!!'
if AB == 'a':
if self.animNameA != None:
self['actor'].setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot')
self.animNameA = self.AnimEntryA.get()
else:
if self.animNameB != None:
self['actor'].setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot')
self.animNameB = self.AnimEntryB.get()
self.currTime = 0.0
self.frameControl.set(0)
self.updateDisplay()
self.setRatio(self.blendRatio)
return
def setPlayRate(self,rate):
#################################################################
# setPlayRate(self,rate)
# see play(self)
#################################################################
self.animNameA = self.AnimEntryA.get()
if self.animNameA in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animNameA)
self.updateDisplay()
if self.animNameB in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animNameB)
self.updateDisplay()
return
def updateDisplay(self):
#################################################################
# updateDisplay(self)
# see play(self)
#################################################################
if not (self.animNameA in self['animList']):
return
self.fps = self['actor'].getFrameRate(self.animNameA)
self.duration = self['actor'].getDuration(self.animNameA)
self.maxFrame = self['actor'].getNumFrames(self.animNameA) - 1
if not (self.animNameB in self['animList']):
return
if self.duration > self['actor'].getDuration(self.animNameB):
self.duration = self['actor'].getDuration(self.animNameB)
if self.maxFrame > self['actor'].getNumFrames(self.animNameB) - 1:
self.maxFrame = self['actor'].getNumFrames(self.animNameB) - 1
self.maxSeconds = self.duration
if self.unitsVar.get() == FRAMES:
fromFrame = 0
toFrame = self.maxFrame
self.minLabel['text'] = fromFrame
self.maxLabel['text'] = toFrame
self.frameControl.configure(from_ = fromFrame,
to = toFrame,
resolution = 1.0)
else:
self.minLabel['text'] = '0.0'
self.maxLabel['text'] = "%.2f" % self.duration
self.frameControl.configure(from_ = 0.0,
to = self.duration,
resolution = 0.01)
def gotoT(self,time):
#################################################################
# gotoT(self,time)
# see play(self)
#################################################################
if self.unitsVar.get() == FRAMES:
self.frameControl.set(time * self.fps)
else:
self.frameControl.set(time)
return
def goTo(self,frame):
#################################################################
# goTo(self,frame)
# see play(self)
#################################################################
if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
# Convert scale value to float
frame = string.atof(frame)
# Now convert t to seconds for offset calculations
if self.unitsVar.get() == FRAMES:
frame = frame / self.fps
if self.dragMode:
self.currTime = frame
self['actor'].pose(self.animNameA,
min(self.maxFrame, int(frame * self.fps)))
self['actor'].pose(self.animNameB,
min(self.maxFrame, int(frame * self.fps)))
return
def onRelease(self,frame):
#################################################################
# onRelease(self,frame)
# see play(self)
#################################################################
self.dragMode = False
return
def onPress(self,frame):
#################################################################
# onPress(self,frame)
# see play(self)
#################################################################
self.dragMode = True
return
def resetAllToZero(self):
#################################################################
# resetAllToZero(self)
# see play(self)
#################################################################
self.currTime = 0.0
self.gotoT(0)
return
def resetAllToEnd(self):
#################################################################
# resetAllToEnd(self)
# see play(self)
#################################################################
self.currTime = self.maxSeconds
self.gotoT(self.duration)
return
def toggleBlend(self):
#################################################################
# toggleBlend(self)
# This function will enable the blending option for the actor.
# and call set ratio function to set the blending animation mixing in
# current ratio.
#
# This blending enable will not be keep when you close the window!
#
#################################################################
if self.blendVar.get():
self.enableBlend = True
self['actor'].enableBlend()
self.setRatio(self.blendRatio)
else:
self.enableBlend = False
self['actor'].disableBlend()
return
def setRatio(self, ratio):
#################################################################
# setRatio(self, ratio)
# callback funtion
# This one will be called each time when user drag the blend ratio
# slider on the panel. This will set the blening ratio to both animation.
# (Which is "setControlEffect")
#################################################################
self.blendRatio = float(ratio)
if self.enableBlend:
if self.animNameA in self['animList']:
self['actor'].setControlEffect(self.animNameA, self.blendRatio, 'modelRoot','lodRoot')
if self.animNameB in self['animList']:
self['actor'].setControlEffect(self.animNameB, 1-self.blendRatio, 'modelRoot','lodRoot')
return
def setBlendAnim(self, name):
#################################################################
# setBlendAnim(self, name)
# This function will be called each time when user try to select
# a existing blending animation from the comboBox on the panel
# This function will re-set every varaibles on the panel to what
# it should be. For example, when user choose blending anim "R,"
# which was blended by anim "a" and "b" with ratio "c,"
# then this function will set Animation A to "a" and animation B
# to "b" and set the ratio slider to "c" position.
#################################################################
if self.blendDict.has_key(name):
self.currentBlendName = name
animA = self.blendDict[name][0]
animB = self.blendDict[name][1]
ratio = self.blendDict[name][2]
self.AnimEntryA.selectitem(animA)
self.AnimEntryB.selectitem(animB)
self.setAnimation(animA, AB = 'a')
self.setAnimation(animB, AB = 'b')
self.ratioControl.set(ratio)
return
def setBlendAnimList(self, dict, select=False):
#################################################################
# setBlendAnimList(self, dict, select=False)
# This function will be called when we need to reset the dropdown list
# of "Blend Anim."
# About "selec" option, this now is mainly used when we remove
# a blended animation from the actor. When it has been specified to True,
# the function will not only reset the list, but will also automatically
# select one from the top of list, if it is not empty.
#################################################################
self.blendDict.clear()
del self.blendDict
self.blendDict = dict.copy()
print self.blendDict
if len(self.blendDict)>0:
self.blendList = self.blendDict.keys()
else:
self.blendList = []
self.blendAnimEntry.setlist(self.blendList)
if select:
if len(self.blendList)>0:
self.blendAnimEntry.selectitem(self.blendList[0])
self.setBlendAnim(self.blendList[0])
self.currentBlendName = self.blendList[0]
else:
self.blendAnimEntry.clear()
self.currentBlendName = None
return
def saveButtonPushed(self):
#################################################################
# saveButtonPushed(self)
# This function will be called when user clicked on the "Save" button
# This functiont will collect all data on the panel and send them with
# a message to sceneEditor to save the current blending animation
# into the dataHolder.
#################################################################
name = self.blendAnimEntry.get()
if name=='':
Pmw.MessageDialog(None, title='Caution!',
message_text = 'You have to give the blending animation a name first!',
iconpos='s',
defaultbutton = 'Close'
)
return
elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])):
Pmw.MessageDialog(None, title='Caution!',
message_text = 'The Animations you have selected are not exist!',
iconpos='s',
defaultbutton = 'Close'
)
return
else:
messenger.send('BAW_saveBlendAnim', [self['actor'].getName(),
name,
self.animNameA,
self.animNameB,
self.blendRatio])
self.currentBlendName = name
return
def removeButtonPushed(self):
#################################################################
# removeButtonPushed(self)
# remove the current seleted blended animation from the actor.
# This will send out a message to sceneEditor to delete the data inside
# the dataHolder and then reset the list of here.
#################################################################
name = self.blendAnimEntry.get()
messenger.send('BAW_removeBlendAnim', [self['actor'].getName(),name])
return
def renameButtonPushed(self):
#################################################################
# renameButtonPushed(self)
# this function will be called when user click on the "Rename" button.
# This function will collect all data on the panel and send them out
# with a message to sceneEditor to rename and re-save all setting about
# current animation.
#################################################################
oName = self.currentBlendName
name = self.blendAnimEntry.get()
if self.currentBlendName == None:
Pmw.MessageDialog(None, title='Caution!',
message_text = "You haven't select any blended animation!!",
iconpos='s',
defaultbutton = 'Close'
)
return
elif name=='':
Pmw.MessageDialog(None, title='Caution!',
message_text = 'You have to give the blending animation a name first!',
iconpos='s',
defaultbutton = 'Close'
)
return
elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])):
Pmw.MessageDialog(None, title='Caution!',
message_text = 'The Animations you have selected are not exist!',
iconpos='s',
defaultbutton = 'Close'
)
return
else:
messenger.send('BAW_renameBlendAnim', [self['actor'].getName(),
name,
oName,
self.animNameA,
self.animNameB,
self.blendRatio]
)
self.currentBlendName = name
return
def onDestroy(self, event):
#################################################################
# onDestroy(self, event)
# This function will be call when user try to close the window.
# In here we will stop all tasks we have opend and disable the
# blend setting of actor.
# If we didn't disable the blend option, the next time you play
# the animation via animation panel will cause some error.
#################################################################
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
taskMgr.remove(self.id + '_UpdateTask')
messenger.send('BAW_close',[self.nodeName])
self.actorNode.setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot')
self.actorNode.setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot')
self.actorNode.disableBlend()
'''
If you have open any thing, please rewrite here!
'''
pass

View file

@ -0,0 +1,670 @@
#################################################################
# seCameraControl.py
# Originally from DirectCameraControl.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We didn't change anything essential.
# Just because we customized the seSession from DirectSession,
# So we need related files can follow the change.
# However, we don't want to change anything inside the original directool
# to let them can work with our scene editor.
# (If we do change original directools, it will force user has to install the latest version of OUR Panda)
#
#################################################################
from direct.showbase.DirectObject import DirectObject
from direct.directtools.DirectUtil import *
from seGeometry import *
from direct.directtools.DirectGlobals import *
from direct.task import Task
CAM_MOVE_DURATION = 1.2
COA_MARKER_SF = 0.0075
Y_AXIS = Vec3(0,1,0)
class DirectCameraControl(DirectObject):
def __init__(self):
# Create the grid
self.startT = 0.0
self.startF = 0
self.orthoViewRoll = 0.0
self.lastView = 0
self.coa = Point3(0,100,0)
self.coaMarker = loader.loadModel('models/misc/sphere')
self.coaMarker.setName('DirectCameraCOAMarker')
self.coaMarker.setTransparency(1)
self.coaMarker.setColor(1,0,0,0)
self.coaMarker.setPos(0,100,0)
useDirectRenderStyle(self.coaMarker)
self.coaMarkerPos = Point3(0)
self.fLockCOA = 0
self.nullHitPointCount = 0
self.cqEntries = []
self.coaMarkerRef = SEditor.group.attachNewNode('coaMarkerRef')
self.camManipRef = SEditor.group.attachNewNode('camManipRef')
t = CAM_MOVE_DURATION
self.actionEvents = [
['DIRECT-mouse2', self.mouseFlyStart],
['DIRECT-mouse2Up', self.mouseFlyStop],
]
self.keyEvents = [
['c', self.centerCamIn, 0.5],
['f', self.fitOnWidget],
['h', self.homeCam],
['shift-v', self.toggleMarkerVis],
['m', self.moveToFit],
['n', self.pickNextCOA],
['u', self.orbitUprightCam],
['shift-u', self.uprightCam],
[`1`, self.spawnMoveToView, 1],
[`2`, self.spawnMoveToView, 2],
[`3`, self.spawnMoveToView, 3],
[`4`, self.spawnMoveToView, 4],
[`5`, self.spawnMoveToView, 5],
[`6`, self.spawnMoveToView, 6],
[`7`, self.spawnMoveToView, 7],
[`8`, self.spawnMoveToView, 8],
['9', self.swingCamAboutWidget, -90.0, t],
['0', self.swingCamAboutWidget, 90.0, t],
['`', self.removeManipulateCameraTask],
['=', self.zoomCam, 0.5, t],
['+', self.zoomCam, 0.5, t],
['-', self.zoomCam, -2.0, t],
['_', self.zoomCam, -2.0, t],
]
def toggleMarkerVis(self):
if SEditor.cameraControl.coaMarker.isHidden():
SEditor.cameraControl.coaMarker.show()
else:
SEditor.cameraControl.coaMarker.hide()
def mouseFlyStart(self, modifiers):
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Where are we in the display region?
if ((abs(SEditor.dr.mouseX) < 0.9) and (abs(SEditor.dr.mouseY) < 0.9)):
# MOUSE IS IN CENTRAL REGION
# Hide the marker for this kind of motion
self.coaMarker.hide()
# Record time of start of mouse interaction
self.startT= globalClock.getFrameTime()
self.startF = globalClock.getFrameCount()
# Start manipulation
self.spawnXZTranslateOrHPanYZoom()
# END MOUSE IN CENTRAL REGION
else:
if ((abs(SEditor.dr.mouseX) > 0.9) and
(abs(SEditor.dr.mouseY) > 0.9)):
# Mouse is in corners, spawn roll task
self.spawnMouseRollTask()
else:
# Mouse is in outer frame, spawn mouseRotateTask
self.spawnMouseRotateTask()
def mouseFlyStop(self):
taskMgr.remove('manipulateCamera')
stopT = globalClock.getFrameTime()
deltaT = stopT - self.startT
stopF = globalClock.getFrameCount()
deltaF = stopF - self.startF
if (deltaT <= 0.25) or (deltaF <= 1):
# Check for a hit point based on
# current mouse position
# Allow intersection with unpickable objects
# And then spawn task to determine mouse mode
# Don't intersect with hidden or backfacing objects
skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
# Skip camera (and its children), unless control key is pressed
skipFlags |= SKIP_CAMERA * (1 - base.getControl())
self.computeCOA(SEditor.iRay.pickGeom(skipFlags = skipFlags))
# Record reference point
self.coaMarkerRef.iPosHprScale(base.cam)
# Record entries
self.cqEntries = []
for i in range(SEditor.iRay.getNumEntries()):
self.cqEntries.append(SEditor.iRay.getEntry(i))
# Show the marker
self.coaMarker.show()
# Resize it
self.updateCoaMarkerSize()
def spawnXZTranslateOrHPanYZoom(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Spawn the new task
t = Task.Task(self.XZTranslateOrHPanYZoomTask)
# For HPanYZoom
t.zoomSF = Vec3(self.coaMarker.getPos(SEditor.camera)).length()
taskMgr.add(t, 'manipulateCamera')
def spawnXZTranslateOrHPPan(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Spawn new task
taskMgr.add(self.XZTranslateOrHPPanTask,
'manipulateCamera')
def spawnXZTranslate(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Spawn new task
taskMgr.add(self.XZTranslateTask, 'manipulateCamera')
def spawnHPanYZoom(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Spawn new task
t = Task.Task(self.HPanYZoomTask)
t.zoomSF = Vec3(self.coaMarker.getPos(SEditor.camera)).length()
taskMgr.add(t, 'manipulateCamera')
def spawnHPPan(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Spawn new task
taskMgr.add(self.HPPanTask, 'manipulateCamera')
def XZTranslateOrHPanYZoomTask(self, state):
if SEditor.fShift:
return self.XZTranslateTask(state)
else:
return self.HPanYZoomTask(state)
def XZTranslateOrHPPanTask(self, state):
if SEditor.fShift:
# Panning action
return self.HPPanTask(state)
else:
# Translation action
return self.XZTranslateTask(state)
def XZTranslateTask(self,state):
coaDist = Vec3(self.coaMarker.getPos(SEditor.camera)).length()
xlateSF = (coaDist / SEditor.dr.near)
SEditor.camera.setPos(SEditor.camera,
(-0.5 * SEditor.dr.mouseDeltaX *
SEditor.dr.nearWidth *
xlateSF),
0.0,
(-0.5 * SEditor.dr.mouseDeltaY *
SEditor.dr.nearHeight *
xlateSF))
return Task.cont
def HPanYZoomTask(self,state):
if SEditor.fControl:
moveDir = Vec3(self.coaMarker.getPos(SEditor.camera))
# If marker is behind camera invert vector
if moveDir[1] < 0.0:
moveDir.assign(moveDir * -1)
moveDir.normalize()
else:
moveDir = Vec3(Y_AXIS)
moveDir.assign(moveDir * (-1.0 * SEditor.dr.mouseDeltaY *
state.zoomSF))
if SEditor.dr.mouseDeltaY > 0.0:
moveDir.setY(moveDir[1] * 1.0)
SEditor.camera.setPosHpr(SEditor.camera,
moveDir[0],
moveDir[1],
moveDir[2],
(0.5 * SEditor.dr.mouseDeltaX *
SEditor.dr.fovH),
0.0, 0.0)
return Task.cont
def HPPanTask(self, state):
SEditor.camera.setHpr(SEditor.camera,
(0.5 * SEditor.dr.mouseDeltaX *
SEditor.dr.fovH),
(-0.5 * SEditor.dr.mouseDeltaY *
SEditor.dr.fovV),
0.0)
return Task.cont
def spawnMouseRotateTask(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Set at markers position in render coordinates
self.camManipRef.setPos(self.coaMarkerPos)
self.camManipRef.setHpr(SEditor.camera, ZERO_POINT)
t = Task.Task(self.mouseRotateTask)
if abs(SEditor.dr.mouseX) > 0.9:
t.constrainedDir = 'y'
else:
t.constrainedDir = 'x'
taskMgr.add(t, 'manipulateCamera')
def mouseRotateTask(self, state):
# If moving outside of center, ignore motion perpendicular to edge
if ((state.constrainedDir == 'y') and (abs(SEditor.dr.mouseX) > 0.9)):
deltaX = 0
deltaY = SEditor.dr.mouseDeltaY
elif ((state.constrainedDir == 'x') and (abs(SEditor.dr.mouseY) > 0.9)):
deltaX = SEditor.dr.mouseDeltaX
deltaY = 0
else:
deltaX = SEditor.dr.mouseDeltaX
deltaY = SEditor.dr.mouseDeltaY
if SEditor.fShift:
SEditor.camera.setHpr(SEditor.camera,
(deltaX * SEditor.dr.fovH),
(-deltaY * SEditor.dr.fovV),
0.0)
self.camManipRef.setPos(self.coaMarkerPos)
self.camManipRef.setHpr(SEditor.camera, ZERO_POINT)
else:
wrt = SEditor.camera.getTransform( self.camManipRef )
self.camManipRef.setHpr(self.camManipRef,
(-1 * deltaX * 180.0),
(deltaY * 180.0),
0.0)
SEditor.camera.setTransform(self.camManipRef, wrt)
return Task.cont
def spawnMouseRollTask(self):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Set at markers position in render coordinates
self.camManipRef.setPos(self.coaMarkerPos)
self.camManipRef.setHpr(SEditor.camera, ZERO_POINT)
t = Task.Task(self.mouseRollTask)
t.coaCenter = getScreenXY(self.coaMarker)
t.lastAngle = getCrankAngle(t.coaCenter)
# Store the camera/manipRef offset transform
t.wrt = SEditor.camera.getTransform( self.camManipRef )
taskMgr.add(t, 'manipulateCamera')
def mouseRollTask(self, state):
wrt = state.wrt
angle = getCrankAngle(state.coaCenter)
deltaAngle = angle - state.lastAngle
state.lastAngle = angle
if base.config.GetBool('temp-hpr-fix',0):
self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
else:
self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
SEditor.camera.setTransform(self.camManipRef, wrt)
return Task.cont
def lockCOA(self):
self.fLockCOA = 1
SEditor.message('COA Lock On')
def unlockCOA(self):
self.fLockCOA = 0
SEditor.message('COA Lock Off')
def toggleCOALock(self):
self.fLockCOA = 1 - self.fLockCOA
if self.fLockCOA:
SEditor.message('COA Lock On')
else:
SEditor.message('COA Lock Off')
def pickNextCOA(self):
""" Cycle through collision handler entries """
if self.cqEntries:
# Get next entry and rotate entries
entry = self.cqEntries[0]
self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1]
# Filter out object's under camera
nodePath = entry.getIntoNodePath()
if SEditor.camera not in nodePath.getAncestors():
# Compute new hit point
hitPt = entry.getSurfacePoint(entry.getFromNodePath())
# Move coa marker to new point
self.updateCoa(hitPt, ref = self.coaMarkerRef)
else:
# Remove offending entry
self.cqEntries = self.cqEntries[:-1]
self.pickNextCOA()
def computeCOA(self, entry):
coa = Point3(0)
dr = SEditor.drList.getCurrentDr()
if self.fLockCOA:
# COA is locked, use existing point
# Use existing point
coa.assign(self.coaMarker.getPos(SEditor.camera))
# Reset hit point count
self.nullHitPointCount = 0
elif entry:
# Got a hit point (hit point is in camera coordinates)
# Set center of action
hitPt = entry.getSurfacePoint(entry.getFromNodePath())
hitPtDist = Vec3(hitPt).length()
coa.assign(hitPt)
# Handle case of bad coa point (too close or too far)
if ((hitPtDist < (1.1 * dr.near)) or
(hitPtDist > dr.far)):
# Just use existing point
coa.assign(self.coaMarker.getPos(SEditor.camera))
# Reset hit point count
self.nullHitPointCount = 0
else:
# Increment null hit point count
self.nullHitPointCount = (self.nullHitPointCount + 1) % 7
# No COA lock and no intersection point
# Use a point out in front of camera
# Distance to point increases on multiple null hit points
# MRM: Would be nice to be able to control this
# At least display it
dist = pow(10.0, self.nullHitPointCount)
SEditor.message('COA Distance: ' + `dist`)
coa.set(0,dist,0)
# Compute COA Dist
coaDist = Vec3(coa - ZERO_POINT).length()
if coaDist < (1.1 * dr.near):
coa.set(0,100,0)
coaDist = 100
# Update coa and marker
self.updateCoa(coa, coaDist = coaDist)
def updateCoa(self, ref2point, coaDist = None, ref = None):
self.coa.set(ref2point[0], ref2point[1], ref2point[2])
if not coaDist:
coaDist = Vec3(self.coa - ZERO_POINT).length()
# Place the marker in render space
if ref == None:
# KEH: use the current display region
# ref = base.cam
ref = SEditor.drList.getCurrentDr().cam
self.coaMarker.setPos(ref, self.coa)
pos = self.coaMarker.getPos()
self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1))
# Resize it
self.updateCoaMarkerSize(coaDist)
# Record marker pos in render space
self.coaMarkerPos.assign(self.coaMarker.getPos())
def updateCoaMarkerSizeOnDeath(self, state):
# Needed because tasks pass in state as first arg
self.updateCoaMarkerSize()
def updateCoaMarkerSize(self, coaDist = None):
if not coaDist:
coaDist = Vec3(self.coaMarker.getPos( SEditor.camera )).length()
# KEH: use current display region for fov
# sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(SEditor.dr.fovV))
sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(SEditor.drList.getCurrentDr().fovV))
if sf == 0.0:
sf = 0.1
self.coaMarker.setScale(sf)
# Lerp color to fade out
self.coaMarker.lerpColor(VBase4(1,0,0,1), VBase4(1,0,0,0), 3.0,
task = 'fadeAway')
def homeCam(self):
# Record undo point
SEditor.pushUndo([SEditor.camera])
SEditor.camera.reparentTo(render)
SEditor.camera.clearMat()
# Resize coa marker
self.updateCoaMarkerSize()
def uprightCam(self):
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Pitch camera till upright
currH = SEditor.camera.getH()
SEditor.camera.lerpHpr(currH, 0, 0,
CAM_MOVE_DURATION,
other = render,
blendType = 'easeInOut',
task = 'manipulateCamera')
def orbitUprightCam(self):
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Transform camera z axis to render space
mCam2Render = Mat4()
mCam2Render.assign(SEditor.camera.getMat(render))
zAxis = Vec3(mCam2Render.xformVec(Z_AXIS))
zAxis.normalize()
# Compute rotation angle needed to upright cam
orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS),-1,1)))
# Check angle
if orbitAngle < 0.1:
# Already upright
return
# Compute orthogonal axis of rotation
rotAxis = Vec3(zAxis.cross(Z_AXIS))
rotAxis.normalize()
# Find angle between rot Axis and render X_AXIS
rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS),-1,1)))
# Determine sign or rotation angle
if rotAxis[1] < 0:
rotAngle *= -1
# Position ref CS at coa marker with xaxis aligned with rot axis
self.camManipRef.setPos(self.coaMarker, Vec3(0))
self.camManipRef.setHpr(render, rotAngle, 0, 0)
# Reparent Cam to ref Coordinate system
parent = SEditor.camera.getParent()
SEditor.camera.wrtReparentTo(self.camManipRef)
# Rotate ref CS to final orientation
t = self.camManipRef.lerpHpr(rotAngle, orbitAngle, 0,
CAM_MOVE_DURATION,
other = render,
blendType = 'easeInOut',
task = 'manipulateCamera')
# Upon death, reparent Cam to parent
t.parent = parent
t.uponDeath = self.reparentCam
def centerCam(self):
self.centerCamIn(1.0)
def centerCamNow(self):
self.centerCamIn(0.)
def centerCamIn(self, t):
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Determine marker location
markerToCam = self.coaMarker.getPos( SEditor.camera )
dist = Vec3(markerToCam - ZERO_POINT).length()
scaledCenterVec = Y_AXIS * dist
delta = markerToCam - scaledCenterVec
self.camManipRef.setPosHpr(SEditor.camera, Point3(0), Point3(0))
t = SEditor.camera.lerpPos(Point3(delta),
CAM_MOVE_DURATION,
other = self.camManipRef,
blendType = 'easeInOut',
task = 'manipulateCamera')
t.uponDeath = self.updateCoaMarkerSizeOnDeath
def zoomCam(self, zoomFactor, t):
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Find a point zoom factor times the current separation
# of the widget and cam
zoomPtToCam = self.coaMarker.getPos(SEditor.camera) * zoomFactor
# Put a target nodePath there
self.camManipRef.setPos(SEditor.camera, zoomPtToCam)
# Move to that point
t = SEditor.camera.lerpPos(ZERO_POINT,
CAM_MOVE_DURATION,
other = self.camManipRef,
blendType = 'easeInOut',
task = 'manipulateCamera')
t.uponDeath = self.updateCoaMarkerSizeOnDeath
def spawnMoveToView(self, view):
# Kill any existing tasks
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Calc hprOffset
hprOffset = VBase3()
if view == 8:
# Try the next roll angle
self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0
# but use the last view
view = self.lastView
else:
self.orthoViewRoll = 0.0
# Adjust offset based on specified view
if view == 1:
hprOffset.set(180., 0., 0.)
elif view == 2:
hprOffset.set(0., 0., 0.)
elif view == 3:
hprOffset.set(90., 0., 0.)
elif view == 4:
hprOffset.set(-90., 0., 0.)
elif view == 5:
hprOffset.set(0., -90., 0.)
elif view == 6:
hprOffset.set(0., 90., 0.)
elif view == 7:
hprOffset.set(135., -35.264, 0.)
# Position target
self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC,
hprOffset)
# Scale center vec by current distance to target
offsetDistance = Vec3(SEditor.camera.getPos(self.camManipRef) -
ZERO_POINT).length()
scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance)
# Now put the camManipRef at that point
self.camManipRef.setPosHpr(self.camManipRef,
scaledCenterVec,
ZERO_VEC)
# Record view for next time around
self.lastView = view
t = SEditor.camera.lerpPosHpr(ZERO_POINT,
VBase3(0,0,self.orthoViewRoll),
CAM_MOVE_DURATION,
other = self.camManipRef,
blendType = 'easeInOut',
task = 'manipulateCamera')
t.uponDeath = self.updateCoaMarkerSizeOnDeath
def swingCamAboutWidget(self, degrees, t):
# Remove existing camera manipulation task
taskMgr.remove('manipulateCamera')
# Record undo point
SEditor.pushUndo([SEditor.camera])
# Coincident with widget
self.camManipRef.setPos(self.coaMarker, ZERO_POINT)
# But aligned with render space
self.camManipRef.setHpr(ZERO_POINT)
parent = SEditor.camera.getParent()
SEditor.camera.wrtReparentTo(self.camManipRef)
manipTask = self.camManipRef.lerpHpr(VBase3(degrees,0,0),
CAM_MOVE_DURATION,
blendType = 'easeInOut',
task = 'manipulateCamera')
# Upon death, reparent Cam to parent
manipTask.parent = parent
manipTask.uponDeath = self.reparentCam
def reparentCam(self, state):
SEditor.camera.wrtReparentTo(state.parent)
self.updateCoaMarkerSize()
def fitOnWidget(self, nodePath = 'None Given'):
# Fit the node on the screen
# stop any ongoing tasks
taskMgr.remove('manipulateCamera')
# How big is the node?
nodeScale = SEditor.widget.scalingNode.getScale(render)
maxScale = max(nodeScale[0],nodeScale[1],nodeScale[2])
maxDim = min(SEditor.dr.nearWidth, SEditor.dr.nearHeight)
# At what distance does the object fill 30% of the screen?
# Assuming radius of 1 on widget
camY = SEditor.dr.near * (2.0 * maxScale)/(0.3 * maxDim)
# What is the vector through the center of the screen?
centerVec = Y_AXIS * camY
# Where is the node relative to the viewpoint
vWidget2Camera = SEditor.widget.getPos(SEditor.camera)
# How far do you move the camera to be this distance from the node?
deltaMove = vWidget2Camera - centerVec
# Move a target there
self.camManipRef.setPos(SEditor.camera, deltaMove)
parent = SEditor.camera.getParent()
SEditor.camera.wrtReparentTo(self.camManipRef)
fitTask = SEditor.camera.lerpPos(Point3(0,0,0),
CAM_MOVE_DURATION,
blendType = 'easeInOut',
task = 'manipulateCamera')
# Upon death, reparent Cam to parent
fitTask.parent = parent
fitTask.uponDeath = self.reparentCam
def moveToFit(self):
# How bit is the active widget?
widgetScale = SEditor.widget.scalingNode.getScale(render)
maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2])
# At what distance does the widget fill 50% of the screen?
camY = ((2 * SEditor.dr.near * (1.5 * maxScale)) /
min(SEditor.dr.nearWidth, SEditor.dr.nearHeight))
# Find a point this distance along the Y axis
# MRM: This needs to be generalized to support non uniform frusta
centerVec = Y_AXIS * camY
# Before moving, record the relationship between the selected nodes
# and the widget, so that this can be maintained
SEditor.selected.getWrtAll()
# Push state onto undo stack
SEditor.pushUndo(SEditor.selected)
# Remove the task to keep the widget attached to the object
taskMgr.remove('followSelectedNodePath')
# Spawn a task to keep the selected objects with the widget
taskMgr.add(self.stickToWidgetTask, 'stickToWidget')
# Spawn a task to move the widget
t = SEditor.widget.lerpPos(Point3(centerVec),
CAM_MOVE_DURATION,
other = SEditor.camera,
blendType = 'easeInOut',
task = 'moveToFitTask')
t.uponDeath = lambda state: taskMgr.remove('stickToWidget')
def stickToWidgetTask(self, state):
# Move the objects with the widget
SEditor.selected.moveWrtWidgetAll()
# Continue
return Task.cont
def enableMouseFly(self, fKeyEvents = 1):
# disable C++ fly interface
base.disableMouse()
# Enable events
for event in self.actionEvents:
self.accept(event[0], event[1], extraArgs = event[2:])
if fKeyEvents:
for event in self.keyEvents:
self.accept(event[0], event[1], extraArgs = event[2:])
# Show marker
self.coaMarker.reparentTo(SEditor.group)
def disableMouseFly(self):
# Hide the marker
self.coaMarker.reparentTo(hidden)
# Ignore events
for event in self.actionEvents:
self.ignore(event[0])
for event in self.keyEvents:
self.ignore(event[0])
# Kill tasks
self.removeManipulateCameraTask()
taskMgr.remove('stickToWidget')
base.enableMouse()
def removeManipulateCameraTask(self):
taskMgr.remove('manipulateCamera')

View file

@ -0,0 +1,49 @@
#################################################################
# seColorEntry.py
# Originally from VectorWidgets.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# Here we need some widget to handle some color input.
# There is some colorEntry in the VectorWidgets.py, but they didn't
# work as we need (we don't need alpha here. so the dim option should be set to 3).
# So we make one from them.
#
#################################################################
from direct.tkwidgets import Valuator
from direct.tkwidgets import Floater
from direct.tkwidgets import Slider
import string, Pmw, Tkinter, tkColorChooser
from direct.tkwidgets.VectorWidgets import VectorEntry
class seColorEntry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class (overriding some superclass options)
optiondefs = (
('dim', 3, Pmw.INITOPT),
('type', 'slider', Pmw.INITOPT),
('fGroup_labels', ('R','G','B'), None),
('min', 0.0, None),
('max', 255.0, None),
('nuDigits', 0, None),
('valuator_resolution', 1.0, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Add menu item to popup color picker
self.addMenuItem(
'Popup color picker',
command = lambda s = self: s.popupColorPicker())
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(seColorEntry)
def popupColorPicker(self):
# Can pass in current color with: color = (255, 0, 0)
color = tkColorChooser.askcolor(
parent = self.interior(),
# Initialize it to current color
initialcolor = tuple(self.get()[:3]))[0]
if color:
self.set((color[0], color[1], color[2]))

View file

@ -0,0 +1,915 @@
####################################################################################################################################################
# File Saving
# This code saves the scene out as python code... the scene is stored in the various dictionaries in "dataHolder.py" ...the class "AllScene"
#
####################################################################################################################################################
from pandac.PandaModules import *
from direct.showbase.ShowBaseGlobal import *
import os
import shutil
import string
####################################################################################################################################################
#### These modules are modified versions of Disney's equivalent modules
#### We need to figure out a way to inherit their modules and overwrite what we need changed
import seParticlePanel
import seParticles
import seParticleEffect
import seForceGroup
####################################################################################################################################################
class FileSaver:
####################################################################################################################################################
# This class saves out the scene built with the scene editor as python code
# There are dictionaries saved out to save the state of the scene for reloading it with the editor
# Currently saving is supported for Models, Animations, Lights, Dummy Nodes
# Attributes like parenting are also saved out
# This class is actually instantiated in sceneEditor.py in the saveScene() method
####################################################################################################################################################
def __init(self):
pass
def SaveFile(self,AllScene,filename,dirname,reSaveFlag=0):
################################################################################################################################################
# This function takes the "dataHolder" instance "AllScene" which has dictionaries containing scene information
# The filename is where the scene will be written to
################################################################################################################################################
i1=" " # indentation
i2=i1+i1 # double indentation
out_file = open(filename,"w")
print "dirname:" + dirname
if( not os.path.isdir(dirname)):
os.mkdir(dirname)
savepathname=Filename(filename)
self.savepath=savepathname.getBasenameWoExtension()
out_file.write("##########################################################################################################\n")
out_file.write("# Auto Generated Code by Scene Editor\n")
out_file.write("# Edit with caution\n")
out_file.write("# Using this file in your code:\n")
out_file.write("# For example, if you have named this file as \"myscene.py\"\n")
out_file.write("# Do the following:\n")
out_file.write("# from myscene import * \n")
out_file.write("# theScene=SavedScene() #instantiate the class\n")
out_file.write("# IMPORTANT: All the documentation below refers to \"theScene\" as the instance of SavedScene()\n")
out_file.write("##########################################################################################################\n\n")
out_file.write("##########################################################################################################\n")
out_file.write("# Import Panda Modules\n")
out_file.write("##########################################################################################################\n")
out_file.write("from direct.directbase.DirectStart import * # Core functionality for running the \"show\"\n")
out_file.write("from direct.actor import Actor # Importing models with animations\n")
out_file.write("from direct.directutil import Mopath # Motion Paths\n")
out_file.write("from direct.interval import MopathInterval # Motion Paths\n")
out_file.write("from direct.interval.IntervalGlobal import * # Intervals for interpolation, sequencing and parallelization\n")
out_file.write("from direct.particles import ParticleEffect # Particle Systems\n")
out_file.write("from direct.particles import ForceGroup # Forces acting on Particles\n")
out_file.write("from direct.particles import Particles\n\n")
out_file.write("##########################################################################################################\n")
out_file.write("# This class stores the entire scene\n")
out_file.write("##########################################################################################################\n\n")
out_file.write("class SavedScene(DirectObject): # We inherit from DirectObject so that we can use self.accept method to catch messages\n")
out_file.write("\n")
out_file.write(i1+"# These dictionaries are required for re-loading a scene in the editor\n")
out_file.write(i1+"# They can be used to access the objects as well\n\n")
out_file.write(i1+"ModelDic={}# Stores all the models and static geometry\n")
out_file.write(i1+"ModelRefDic={}# Stores the paths to the models\n")
out_file.write("\n")
out_file.write(i1+"ActorDic={}# Stores all the actors\n")
out_file.write(i1+"ActorRefDic={}# Stores the paths to the actors\n")
out_file.write(i1+"ActorAnimsDic={}# Stores the animations for each actor\n")
out_file.write(i1+"blendAnimDict={}# Stores all the blended animations\n")
out_file.write("\n")
out_file.write(i1+"LightDict={}# Stores all the lights\n")
out_file.write(i1+"LightTypes={}# Stores types for the lights\n")
out_file.write(i1+"LightNodes={}# Stores the actual nodes for the lights\n")
out_file.write("\n")
out_file.write(i1+"dummyDict={}# Stores dummies\n")
out_file.write("\n")
out_file.write(i1+"collisionDict={}# Stores Collision information\n")
out_file.write("\n")
out_file.write(i1+"curveDict={}# Stores Mopath information\n")
out_file.write(i1+"curveIntervals=[]# Stores list of mopath intervals\n")
out_file.write(i1+"curveRefColl=[]# Stores paths to mopaths\n")
out_file.write(i1+"curveIntervalsDict={}# Stores mopath intervals\n")
out_file.write("\n")
out_file.write(i1+"particleDict={}# Stores particles\n")
out_file.write(i1+"particleNodes={}# Stores particle nodes\n")
out_file.write("\n")
out_file.write(i1+"#Light Count\n")
out_file.write(i1+"ambientCount=0\n")
out_file.write(i1+"directionalCount=0\n")
out_file.write(i1+"pointCount=0\n")
out_file.write(i1+"spotCount=0\n")
out_file.write("\n")
out_file.write(i1+"#Lighting Attribute\n")
out_file.write(i1+"lightAttrib = LightAttrib.makeAllOff()# Initialize lighting\n")
out_file.write("\n")
out_file.write(i1+"CollisionHandler=CollisionHandlerEvent()# Setup a Collision Handler\n")
out_file.write(i1+"##########################################################################################################\n")
out_file.write(i1+"# Constructor: this is run first when you instantiate the SavedScene class\n")
out_file.write(i1+"##########################################################################################################\n")
out_file.write(i1+"def __init__(self,loadmode=1,seParticleEffect=None,seParticles=None,executionpath=None):# loadmode 0 specifies that this file is being loaded by the scene editor and it passes its own versions of the particle fx modules\n")
out_file.write("\n")
out_file.write(i2+"self.loadmode=loadmode\n")
out_file.write(i2+"self.seParticleEffect=seParticleEffect\n")
out_file.write(i2+"self.seParticles=seParticles\n")
out_file.write(i2+"self.executionpath=executionpath\n")
out_file.write("\n")
out_file.write(i2+"base.enableParticles()# Enable Particle effects\n")
out_file.write("\n")
out_file.write(i2+"self.cTrav = CollisionTraverser() # Setup a traverser for collisions\n")
out_file.write(i2+"base.cTrav = self.cTrav\n")
out_file.write(i2+"self.CollisionHandler.setInPattern(\"enter%in\")# The message to be raised when something enters a collision node\n")
out_file.write(i2+"self.CollisionHandler.setOutPattern(\"exit%in\")# The message to be raised when something exits a collision node\n")
out_file.write("\n")
####################################################################################################################################################
# Save Models
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Code for all the models\n")
out_file.write(i2+"# To access these models:\n")
out_file.write(i2+"# theScene.ModelDic[\"'Model_Name\"']\n")
out_file.write(i2+"# where theScene is the SavedScene class instance\n")
out_file.write(i2+"# Properties saved include:\n")
out_file.write(i2+"# Transformations\n")
out_file.write(i2+"# Alpha and color\n")
out_file.write(i2+"# Parent and child information\n")
out_file.write(i2+"##########################################################################################################\n")
for model in AllScene.ModelDic:
out_file.write("\n")
modelS=str(model)
if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode
#Loading Code
out_file.write(i2+"# Loading model's egg file\n")
#out_file.write(i2+ "self."+ modelS + "=loader.loadModel(\'" + AllScene.ModelRefDic[model].getFullpath() + "\')\n")#Absolute Paths
newpath = dirname + "/" + AllScene.ModelRefDic[model].getBasename()
newpathF=Filename(newpath)
newpathSpecific=newpathF.toOsSpecific()
# Copy all the textures referenced by this file over to the relative directory
fnamelist=[]
modelData=EggData()
modelData.read(AllScene.ModelRefDic[model])
textures=EggTextureCollection()
textures.findUsedTextures(modelData)
for index in range(textures.getNumTextures()):
texture=textures.getTexture(index)
texfilename=texture.getFilename()
fnamelist.append(texfilename.getFullpath())
oldFilename=Filename(Filename(AllScene.ModelRefDic[model].getDirname()),texfilename)
if(not oldFilename.isRegularFile()):
if(texfilename.resolveFilename(getTexturePath(),"")):
oldFilename=texfilename
oldtexpath=oldFilename.toOsSpecific()
newtexpath=dirname + "/" + texfilename.getBasename()
newtexpathF=Filename(newtexpath)
newtexpathSpecific=newtexpathF.toOsSpecific()
print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific
if(oldtexpath != newtexpathSpecific):
shutil.copyfile(oldtexpath,newtexpathSpecific)
# Copy the file over to the relative directory
oldModelpath=AllScene.ModelRefDic[model].toOsSpecific()
print "FILESAVER:: copying from " + AllScene.ModelRefDic[model].toOsSpecific() + "to" + newpathSpecific
if(oldModelpath!=newpathSpecific):
shutil.copyfile(oldModelpath,newpathSpecific)
e=EggData()
e.read(AllScene.ModelRefDic[model])
etc=EggTextureCollection()
etc.extractTextures(e)
for index in range(len(fnamelist)):
print fnamelist[index]
tex=etc.findFilename(Filename(fnamelist[index]))
fn=Filename(tex.getFilename())
fn.setDirname("")
tex.setFilename(fn)
e.writeEgg(Filename.fromOsSpecific(newpathSpecific))
out_file.write(i2+"if(self.loadmode==1):\n")
out_file.write(i2+i1+ "self."+ modelS + "=loader.loadModel(\'" + self.savepath + "/" + AllScene.ModelRefDic[model].getBasename() + "')\n")#Relative Path
out_file.write(i2+"else:\n")
out_file.write(i2+i1+ "self."+ modelS + "=loader.loadModel(self.executionpath + \'/" + AllScene.ModelRefDic[model].getBasename() + "')\n")#Relative Path with execution point specified by the invoking-level-editor
#Transformation Code
out_file.write("\n")
out_file.write(i2+"# Transforming the model\n")
out_file.write(i2+ "self."+ modelS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.ModelDic[model].getX(),AllScene.ModelDic[model].getY(),AllScene.ModelDic[model].getZ(),AllScene.ModelDic[model].getH(),AllScene.ModelDic[model].getP(),AllScene.ModelDic[model].getR(),AllScene.ModelDic[model].getSx(),AllScene.ModelDic[model].getSy(),AllScene.ModelDic[model].getSz()))
if(AllScene.ModelDic[model].hasTransparency()):
out_file.write("\n")
out_file.write(i2+"# Alpha\n")
out_file.write(i2+ "self."+ modelS + ".setTransparency(1)\n")
clr=AllScene.ModelDic[model].getColor()
out_file.write(i2+ "self."+ modelS + ".setColor(%.4f,%.4f,%.4f,%.4f)\n"%(clr.getX(),clr.getY(),clr.getZ(),clr.getW()))
out_file.write("\n")
out_file.write(i2+ "# Reparent To Render for now and later we update all the parentings\n")
out_file.write(i2+ "self."+ modelS + ".reparentTo(render)\n")
out_file.write("\n")
out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.ModelDic[\"Model_Name\"].getTag(\"Metadata\")\n")
out_file.write(i2+ "self."+ modelS + ".setTag(\"Metadata\",\"" + AllScene.ModelDic[model].getTag("Metadata") + "\")\n")
out_file.write("\n")
out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n")
out_file.write(i2+ "self.ModelDic[\'" + modelS + "\']=self." + AllScene.ModelDic[model].getName()+"\n")
#out_file.write(i2+ "self.ModelRefDic[\'" + modelS + "\']=Filename(\'"+ AllScene.ModelRefDic[model].getFullpath() +"\')\n")# The old Absolute Path way
out_file.write(i2+ "self.ModelRefDic[\'" + modelS + "\']=\'"+ AllScene.ModelRefDic[model].getBasename() +"\'\n")# Relative paths
out_file.write(i2+ "self.ModelDic[\'"+ modelS + "\'].setName(\'"+ modelS +"\')\n")
out_file.write("\n")
####################################################################################################################################################
# Save Dummies
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Code for all the Dummy Objects\n")
out_file.write(i2+"# To access the dummies\n")
out_file.write(i2+"# theScene.dummyDict['Dummy_Name']\n")
out_file.write(i2+"##########################################################################################################\n")
for dummy in AllScene.dummyDict:
out_file.write("\n")
dummyS=str(dummy)
if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode
out_file.write(i2+ "self."+ dummyS + "=loader.loadModel(\"models/misc/sphere\")\n")
#Transformation Code
out_file.write(i2+"# Transforming the Dummy\n")
out_file.write(i2+ "self."+ dummyS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.dummyDict[dummy].getX(),AllScene.dummyDict[dummy].getY(),AllScene.dummyDict[dummy].getZ(),AllScene.dummyDict[dummy].getH(),AllScene.dummyDict[dummy].getP(),AllScene.dummyDict[dummy].getR(),AllScene.dummyDict[dummy].getSx(),AllScene.dummyDict[dummy].getSy(),AllScene.dummyDict[dummy].getSz()))
out_file.write("\n")
out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n")
out_file.write(i2+ "self.dummyDict[\'" + dummyS + "\']=self." + AllScene.dummyDict[dummy].getName()+"\n")
out_file.write(i2+ "self.dummyDict[\'"+ dummyS + "\'].setName(\'"+ dummyS +"\')\n")
out_file.write("\n")
out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.dummyDict[\"Dummy_Name\"].getTag(\"Metadata\")\n")
out_file.write(i2+ "self."+ dummyS + ".setTag(\"Metadata\",\"" + AllScene.dummyDict[dummy].getTag("Metadata") + "\")\n")
out_file.write("\n")
####################################################################################################################################################
# Saving Actors and their animations
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Code for all the Actors and animations\n")
out_file.write(i2+"# To access the Actors\n")
out_file.write(i2+"# theScene.ActorDic[\'Actor_Name\']\n")
out_file.write(i2+"# theScene.ActorDic[\'Actor_Name\'].play(\'Animation_Name\')\n")
out_file.write(i2+"##########################################################################################################\n")
for actor in AllScene.ActorDic:
out_file.write("\n")
actorS=str(actor)
if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode
#out_file.write(i2+ "self."+ actorS + "=Actor.Actor(\'"+ AllScene.ActorRefDic[actor].getFullpath() + "\')\n")# The old way with absolute paths
newpath = dirname + "/" + AllScene.ActorRefDic[actor].getBasename()
newpathF=Filename(newpath)
newpathSpecific=newpathF.toOsSpecific()
# Copy all the textures referenced by this file over to the relative directory
actorfnamelist=[]
actorData=EggData()
actorData.read(AllScene.ActorRefDic[actor])
textures=EggTextureCollection()
textures.findUsedTextures(actorData)
for index in range(textures.getNumTextures()):
texture=textures.getTexture(index)
texfilename=texture.getFilename()
actorfnamelist.append(texfilename.getFullpath())
oldFilename=Filename(Filename(AllScene.ActorRefDic[actor].getDirname()),texfilename)
if(not oldFilename.isRegularFile()):
if(texfilename.resolveFilename(getTexturePath(),"")):
oldFilename=texfilename
oldtexpath=oldFilename.toOsSpecific()
newtexpath=dirname + "/" + texfilename.getBasename()
newtexpathF=Filename(newtexpath)
newtexpathSpecific=newtexpathF.toOsSpecific()
print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific
if(oldtexpath != newtexpathSpecific):
shutil.copyfile(oldtexpath,newtexpathSpecific)
# Copy the file over to the relative directory
oldActorpath=AllScene.ActorRefDic[actor].toOsSpecific()
print "FILESAVER:: copying from " + AllScene.ActorRefDic[actor].toOsSpecific() + "to" + newpathSpecific
if(oldActorpath!=newpathSpecific):
shutil.copyfile(oldActorpath,newpathSpecific)
e=EggData()
e.read(AllScene.ActorRefDic[actor])
etc=EggTextureCollection()
etc.extractTextures(e)
for index in range(len(actorfnamelist)):
print actorfnamelist[index]
tex=etc.findFilename(Filename(actorfnamelist[index]))
fn=Filename(tex.getFilename())
fn.setDirname("")
tex.setFilename(fn)
e.writeEgg(Filename.fromOsSpecific(newpathSpecific))
out_file.write(i2+"if(self.loadmode==1):\n")
out_file.write(i2+i1+ "self."+ actorS + "=Actor.Actor(\'" + self.savepath + "/" + AllScene.ActorRefDic[actor].getBasename() + "')\n")#Relative Path
out_file.write(i2+"else:\n")
out_file.write(i2+i1+ "self."+ actorS + "=Actor.Actor(self.executionpath + \'/" + AllScene.ActorRefDic[actor].getBasename() + "')\n")#Relative Path with execution point specified by the invoking-level-editor
#Transformation Code
out_file.write(i2+"# Transforming the Actor\n")
out_file.write(i2+ "self."+ actorS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.ActorDic[actor].getX(),AllScene.ActorDic[actor].getY(),AllScene.ActorDic[actor].getZ(),AllScene.ActorDic[actor].getH(),AllScene.ActorDic[actor].getP(),AllScene.ActorDic[actor].getR(),AllScene.ActorDic[actor].getSx(),AllScene.ActorDic[actor].getSy(),AllScene.ActorDic[actor].getSz()))
if(AllScene.ActorDic[actor].hasTransparency()):
out_file.write(i2+"# Alpha\n")
out_file.write(i2+ "self."+ actorS + ".setTransparency(1)\n")
clr=AllScene.ActorDic[actor].getColor()
out_file.write(i2+ "self."+ actorS + ".setColor(%.4f,%.4f,%.4f,%.4f)\n"%(clr.getX(),clr.getY(),clr.getZ(),clr.getW()))
out_file.write(i2+ "self."+ actorS + ".reparentTo(render)\n")
out_file.write("\n")
out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.ActorDic[\"Actor_Name\"].getTag(\"Metadata\")\n")
out_file.write(i2+ "self."+ actorS + ".setTag(\"Metadata\",\"" + AllScene.ActorDic[actor].getTag("Metadata") + "\")\n")
out_file.write("\n")
out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n")
ActorAnimations=AllScene.getAnimationDictFromActor(actor)
ActorAnimationsInvoke={}
if(ActorAnimations!={}): #Check if a dictionary of animations exists for this actor
for animation in ActorAnimations:
#out_file.write(i2+ "self."+ actorS + ".loadAnims(" + str(ActorAnimations) +")\n") # Old way with absolute paths
#Manakel 2/12/2004: solve the not empty but not defined animation case
if not animation is None:
print "ACTOR ANIMATIONS:" + ActorAnimations[animation]
oldAnimPath=Filename(ActorAnimations[animation])
oldAnim=oldAnimPath.toOsSpecific()
dirOS=Filename(dirname)
newAnim=dirOS.toOsSpecific() + "\\" + oldAnimPath.getBasename()
print "ACTOR ANIM SAVER:: Comparing" + oldAnim +"and" + newAnim
if(oldAnim!=newAnim):
shutil.copyfile(oldAnim,newAnim)
newAnimF=Filename.fromOsSpecific(newAnim)
ActorAnimationsInvoke[animation]="self.executionpath +" + "/" +newAnimF.getBasename()
ActorAnimations[animation]= self.savepath + "/" + newAnimF.getBasename()
out_file.write(i2+"if(self.loadmode==1):\n")
out_file.write(i2+ i1+"self."+ actorS + ".loadAnims(" + str(ActorAnimations) +")\n") # Now with new relative paths
out_file.write(i2+"else:\n")
theloadAnimString=str(ActorAnimationsInvoke)# We hack the "self.executionpath" part into the dictionary as a variable using string replace
print "LOAD ANIM STRING BEFORE" + theloadAnimString
theloadAnimString=theloadAnimString.replace('\'self.executionpath +','self.executionpath + \'')
print "LOAD ANIM STRING AFTER" + theloadAnimString
out_file.write(i2+ i1+"self."+ actorS + ".loadAnims(" + theloadAnimString +")\n") # Now with new relative paths based on editor invocation
out_file.write(i2+ "self.ActorDic[\'" + actorS + "\']=self." + AllScene.ActorDic[actor].getName()+"\n")
#out_file.write(i2+ "self.ActorRefDic[\'" + actorS + "\']=Filename(\'"+AllScene.ActorRefDic[actor].getFullpath() +"\')\n") # Old way with absolute paths
out_file.write(i2+ "self.ActorRefDic[\'" + actorS + "\']=\'"+ AllScene.ActorRefDic[actor].getBasename() +"\'\n")# Relative paths
out_file.write(i2+ "self.ActorDic[\'"+ actorS + "\'].setName(\'"+ actorS +"\')\n")
if(AllScene.blendAnimDict.has_key(actor)): # Check if a dictionary of blended animations exists
out_file.write(i2+ "self.blendAnimDict[\"" + actorS +"\"]=" + str(AllScene.blendAnimDict[actor]) + "\n")
out_file.write("\n")
####################################################################################################################################################
# Collsion Node Saving
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Code for setting up Collision Nodes\n")
out_file.write(i2+"# To use collision detection:\n")
out_file.write(i2+"# You must set up your own bitmasking and event handlers, the traverser \"cTrav\" is created for you at the top\n")
out_file.write(i2+"# The collision nodes are stored in collisionDict\n")
out_file.write(i2+"##########################################################################################################\n\n")
for collnode in AllScene.collisionDict:
collnodeS=str(collnode)
solid=AllScene.collisionDict[collnode].node().getSolid(0)
nodetype=solid.getType().getName()
if(nodetype=="CollisionSphere"): #Save Collison Sphere
out_file.write(i2+"collSolid=CollisionSphere(%.3f,%.3f,%.3f,%.3f)\n"%(solid.getCenter().getX(),solid.getCenter().getY(),solid.getCenter().getZ(),solid.getRadius()))
pass
elif(nodetype=="CollisionPolygon"): #Save Collison Polygon
ax=AllScene.collisionDict[collnode].getTag("A_X")
ay=AllScene.collisionDict[collnode].getTag("A_Y")
az=AllScene.collisionDict[collnode].getTag("A_Z")
bx=AllScene.collisionDict[collnode].getTag("B_X")
by=AllScene.collisionDict[collnode].getTag("B_Y")
bz=AllScene.collisionDict[collnode].getTag("B_Z")
cx=AllScene.collisionDict[collnode].getTag("C_X")
cy=AllScene.collisionDict[collnode].getTag("C_Y")
cz=AllScene.collisionDict[collnode].getTag("C_Z")
out_file.write(i2+"pointA = Point3(" + ax + "," + ay + "," + az + ")\n")
out_file.write(i2+"pointB = Point3(" + bx + "," + by + "," + bz + ")\n")
out_file.write(i2+"pointC = Point3(" + cx + "," + cy + "," + cz + ")\n")
out_file.write(i2+"collSolid=CollisionPolygon(pointA, pointB, pointC)\n")
pass
elif(nodetype=="CollisionSegment"): #Save Collison Segment
A=AllScene.collisionDict[collnode].node().getSolid(0).getPointA()
B=AllScene.collisionDict[collnode].node().getSolid(0).getPointB()
out_file.write(i2+"pointA = Point3(%.3f,%.3f,%.3f)\n"%(A.getX(),A.getY(),A.getZ()))
out_file.write(i2+"pointB = Point3(%.3f,%.3f,%.3f)\n"%(B.getX(),B.getY(),B.getZ()))
out_file.write(i2+"collSolid=CollisionSegment()\n")
out_file.write(i2+"collSolid.setPointA(pointA)\n")
out_file.write(i2+"collSolid.setFromLens(base.cam.node(),Point2(-1,1))\n")
out_file.write(i2+"collSolid.setPointB(pointB)\n")
pass
elif(nodetype=="CollisionRay"): #Save Collison Ray
P = AllScene.collisionDict[collnode].node().getSolid(0).getOrigin()
V = AllScene.collisionDict[collnode].node().getSolid(0).getDirection()
out_file.write(i2+"point=Point3(%.3f,%.3f,%.3f)\n"%(P.getX(),P.getY(),P.getZ()))
out_file.write(i2+"vector=Vec3(%.3f,%.3f,%.3f)\n"%(V.getX(),V.getY(),V.getZ()))
out_file.write(i2+"collSolid=CollisionRay()\n")
out_file.write(i2+"collSolid.setOrigin(point)\n")
out_file.write(i2+"collSolid.setDirection(vector)\n")
pass
else:
print "Invalid Collision Node: " + nodetype
out_file.write("\n")
out_file.write(i2+"self." + collnodeS + "_Node" + "=CollisionNode(\""+collnodeS+"\")\n")
out_file.write(i2+"self." + collnodeS + "_Node" + ".addSolid(collSolid)\n")
out_file.write(i2+"base.cTrav.addCollider(self." + collnodeS + "_Node,self.CollisionHandler)\n")
out_file.write("\n")
####################################################################################################################################################
# Light Saving
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Code for Lighting\n")
out_file.write(i2+"# To manipulated lights:\n")
out_file.write(i2+"# Manipulate the light node in theScene.LightNodes[\'Light_Name\']\n")
out_file.write(i2+"##########################################################################################################\n\n")
LightList=AllScene.lightManager.getLightNodeList()
for light in LightList:
type = light.getType()
if type == 'ambient':
out_file.write(i2+"# Ambient Light\n")
out_file.write (i2+ "self.ambientCount += 1\n")
out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n")
out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight.upcastToPandaNode())\n")
out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n")
out_file.write ("\n")
elif type == 'directional':
out_file.write(i2+"# Directional Light\n")
out_file.write (i2+ "self.directionalCount += 1\n")
out_file.write (i2+ "alight = DirectionalLight(\'"+ light.getName() + "\')\n")
out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
#out_file.write (i2+ "alight.setDirection(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
#out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n")
out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
#out_file.write (i2+ "alight.setPos
out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n")
out_file.write ("\n")
elif type == 'point':
out_file.write(i2+"# Point Light\n")
out_file.write (i2+ "self.pointCount += 1\n")
out_file.write (i2+ "alight = PointLight(\'"+ light.getName() +"\')\n")
out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
#out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n")
out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n")
out_file.write ("\n")
elif type == 'spot':
out_file.write(i2+"# Spot Light\n")
out_file.write (i2+ "self.spotCount += 1\n")
out_file.write (i2+ "alight = Spotlight(\'"+ light.getName() + "\')\n")
out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
out_file.write (i2+ "alens = PerspectiveLens()\n")
out_file.write (i2+ "alight.setLens(alens)\n")
out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n")
out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToLensNode())\n")
out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n")
out_file.write ("\n")
else:
out_file.write (i2+ "print \'Invalid light type\'")
out_file.write (i2+ "return None")
out_file.write("\n")
####################################################################################################################################################
# Enable Lighting
####################################################################################################################################################
out_file.write(i2+ "# Enable Ligthing\n")
out_file.write(i2+ "render.node().setAttrib(self.lightAttrib)\n")
out_file.write("\n")
####################################################################################################################################################
# Initialize Particles for non scene editor mode
####################################################################################################################################################
out_file.write(i2+"# Load Particle Effects. The parameters to this function are to allow us to use our modified versions of the Particle Effects modules when loading this file with the level editor\n")
out_file.write(i2+"self.starteffects(self.loadmode,self.seParticleEffect,self.seParticles)\n")
out_file.write("\n")
####################################################################################################################################################
# Save Camera Settings
####################################################################################################################################################
out_file.write("\n")
out_file.write(i2+ "# Save Camera Settings\n")
out_file.write(i2+ "camera.setX(" + str(camera.getX()) + ")\n")
out_file.write(i2+ "camera.setY(" + str(camera.getY()) + ")\n")
out_file.write(i2+ "camera.setZ(" + str(camera.getZ()) + ")\n")
out_file.write(i2+ "camera.setH(" + str(camera.getH()) + ")\n")
out_file.write(i2+ "camera.setP(" + str(camera.getP()) + ")\n")
out_file.write(i2+ "camera.setR(" + str(camera.getR()) + ")\n")
out_file.write(i2+ "camera.getChild(0).node().getLens().setNear(" + str(camera.getChild(0).node().getLens().getNear()) + ")\n")
out_file.write(i2+ "camera.getChild(0).node().getLens().setFar(" + str(camera.getChild(0).node().getLens().getFar()) + ")\n")
out_file.write(i2+ "camera.getChild(0).node().getLens().setFov(VBase2(%.5f,%.5f))\n"% (camera.getChild(0).node().getLens().getHfov(),camera.getChild(0).node().getLens().getVfov()))
FilmSize=camera.getChild(0).node().getLens().getFilmSize()
out_file.write(i2+ "camera.getChild(0).node().getLens().setFilmSize(%.3f,%.3f)\n"%(FilmSize.getX(),FilmSize.getY()))
out_file.write(i2+ "camera.getChild(0).node().getLens().setFocalLength(" + str(camera.getChild(0).node().getLens().getFocalLength()) + ")\n")
out_file.write(i2+ "camera.setTag(\"Metadata\",\"" + camera.getTag("Metadata") + "\")\n")
out_file.write(i2+ "camera.reparentTo(render)\n")
out_file.write(i2+ "base.disableMouse()\n")
self.bgColor=base.getBackgroundColor()
out_file.write(i2+ "base.setBackgroundColor(%.3f,%.3f,%.3f)\n"%(self.bgColor.getX(),self.bgColor.getY(),self.bgColor.getZ()))
out_file.write("\n")
####################################################################################################################################################
# Mopath Saving
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Motion Paths\n")
out_file.write(i2+"# Using Mopaths:\n")
out_file.write(i2+"# theScene.curveIntervals[0].start() or .loop() will play curve with index 0\n")
out_file.write(i2+"##########################################################################################################\n\n")
for node in AllScene.curveDict:
curveCollection=AllScene.curveDict[node]
curvenumber=0
for curve in curveCollection:
filestring=dirname+ "\\" + str(node)+"_curve_"+str(curvenumber)+".egg"
f=Filename.fromOsSpecific(filestring)
#filestring=f.getFullpath()# The old absolute path way
filestring=f.getBasename() # The new relative path way
curve.writeEgg(f)
out_file.write(i2+"m=Mopath.Mopath()\n")
out_file.write(i2+"if(self.loadmode==1):\n")
out_file.write(i2+i1+"m.loadFile(\"" + self.savepath +"/"+ filestring + "\")\n") # If just normally executed
out_file.write(i2+"else:\n")
out_file.write(i2+i1+"m.loadFile(self.executionpath + \"/"+ filestring + "\")\n") # If being invoked by level Ed
out_file.write(i2+"mp=MopathInterval(m,self." + str(node) + ")\n")
out_file.write(i2+"self.curveIntervals.append(mp)\n")
out_file.write(i2+"if(self.loadmode==1):\n")
out_file.write(i2+i1+"self.curveRefColl.append(\"" + self.savepath +"/"+ filestring +"\")\n")
out_file.write(i2+"else:\n")
out_file.write(i2+i1+"self.curveRefColl.append(self.executionpath + \"/"+ filestring +"\")\n")
curvenumber=curvenumber+1
out_file.write(i2+"self.curveIntervalsDict[\"" + str(node) + "\"]=self.curveIntervals\n")
out_file.write(i2+"self.curveDict[\"" + str(node) + "\"]=self.curveRefColl\n")
####################################################################################################################################################
# Lets do all the reparenting here so as to make sure everything that needed to load was loaded
####################################################################################################################################################
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Reparenting\n")
out_file.write(i2+"# A final pass is done on setting all the scenegraph hierarchy after all objects are laoded\n")
out_file.write(i2+"##########################################################################################################\n\n")
for model in AllScene.ModelDic:
modelS=str(model)
parent=AllScene.ModelDic[model].getParent().getName()
if(parent=="render" or parent=="camera"):
out_file.write(i2+ "self."+ modelS + ".reparentTo(" + parent + ")\n")
else:
if(AllScene.particleDict.has_key(parent)):
out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ".getEffect())\n")
else:
out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ")\n")
out_file.write(i2+ "self.ModelDic[\'" + modelS + "\']=self." + AllScene.ModelDic[model].getName()+"\n")
out_file.write(i2+"\n")
for dummy in AllScene.dummyDict:
dummyS=str(dummy)
parent=AllScene.dummyDict[dummy].getParent().getName()
if(parent=="render" or parent=="camera"):
out_file.write(i2+ "self."+ dummyS + ".reparentTo(" + parent + ")\n")
else:
if(AllScene.particleDict.has_key(parent)):
out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ".getEffect())\n")
else:
out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ")\n")
out_file.write(i2+ "self.dummyDict[\'" + dummyS + "\']=self." + AllScene.dummyDict[dummy].getName()+"\n")
out_file.write(i2+"\n")
for actor in AllScene.ActorDic:
actorS=str(actor)
parent=AllScene.ActorDic[actor].getParent().getName()
if(parent=="render" or parent=="camera"):
out_file.write(i2+ "self."+ actorS + ".reparentTo(" + parent + ")\n")
else:
if(AllScene.particleDict.has_key(parent)):
out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ".getEffect())\n")
else:
out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ")\n")
out_file.write(i2+ "self.ActorDic[\'" + actorS + "\']=self." + AllScene.ActorDic[actor].getName()+"\n")
out_file.write(i2+"\n")
for collnode in AllScene.collisionDict:
collnodeS=str(collnode)
solid=AllScene.collisionDict[collnode].node().getSolid(0)
nodetype=solid.getType().getName()
parentname=AllScene.collisionDict[collnode].getParent().getName()
if(parentname=="render" or parentname =="camera"):
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]="+ parentname + ".attachNewNode(self." + collnodeS + "_Node)\n")
else:
#Manakel 2/12/2005: parent replaced by parent Name but why Parent name in partice and parent for other objects?
if(AllScene.particleDict.has_key(parentname)):
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]=self."+ parentname + "getEffect().attachNewNode(self." + collnodeS + "_Node)\n")
else:
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]=self."+ parentname + ".attachNewNode(self." + collnodeS + "_Node)\n")
dictelem=AllScene.collisionDict[collnode]
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"%(dictelem.getX(),dictelem.getY(),dictelem.getZ(),dictelem.getH(),dictelem.getP(),dictelem.getR(),dictelem.getSx(),dictelem.getSy(),dictelem.getSz()))
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag(\"Metadata\",\"" + AllScene.collisionDict[collnode].getTag("Metadata") + "\")\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].show()\n")
if(nodetype=="CollisionPolygon"): #Save Collison Polygon... the reason we need to use setTag here is because there is no inbuilt way of saving transforms for collision polys
ax=float(AllScene.collisionDict[collnode].getTag("A_X"))
ay=float(AllScene.collisionDict[collnode].getTag("A_Y"))
az=float(AllScene.collisionDict[collnode].getTag("A_Z"))
bx=float(AllScene.collisionDict[collnode].getTag("B_X"))
by=float(AllScene.collisionDict[collnode].getTag("B_Y"))
bz=float(AllScene.collisionDict[collnode].getTag("B_Z"))
cx=float(AllScene.collisionDict[collnode].getTag("C_X"))
cy=float(AllScene.collisionDict[collnode].getTag("C_Y"))
cz=float(AllScene.collisionDict[collnode].getTag("C_Z"))
out_file.write(i2+"pointA=Point3(%.3f,%.3f,%.3f)\n"%(ax,ay,az))
out_file.write(i2+"pointB=Point3(%.3f,%.3f,%.3f)\n"%(bx,by,bz))
out_file.write(i2+"pointC=Point3(%.3f,%.3f,%.3f)\n"%(cx,cy,cz))
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_X','%f'%pointA.getX())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_Y','%f'%pointA.getY())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_Z','%f'%pointA.getZ())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_X','%f'%pointB.getX())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_Y','%f'%pointB.getY())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_Z','%f'%pointB.getZ())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_X','%f'%pointC.getX())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_Y','%f'%pointC.getY())\n")
out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_Z','%f'%pointC.getZ())\n")
out_file.write(i2+"\n")
for effect in AllScene.particleDict:
parent=AllScene.particleNodes[effect].getParent().getName()
if(parent=="render" or parent=="camera"):
out_file.write(i2+"self.particleDict[\""+ str(effect) +"\"].reparentTo(" + parent + ")\n")
else:
out_file.write(i2+"self.particleDict[\""+ str(effect) +"\"].reparentTo(self." + parent + ")\n")
out_file.write(i2+"\n")
####################################################################################################################################################
# Particle Saving
####################################################################################################################################################
out_file.write("\n")
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Particle Effects\n")
out_file.write(i2+"# Using Particles:\n")
out_file.write(i2+"# theScene.enableeffect(\"Effect_Name\")\n")
out_file.write(i2+"##########################################################################################################\n\n")
out_file.write(i1+"def starteffects(self,mode,seParticleEffect=None,seParticles=None):\n")
for effect in AllScene.particleDict:
effectS=str(effect)
out_file.write(i2+ "self." + effectS + "=" + effectS + "(mode,seParticleEffect,seParticles)\n")
out_file.write(i2+ "effect=self."+ effectS + ".getEffect()\n")
out_file.write(i2+ "self.particleDict[\"" + effectS + "\"]=effect\n")
out_file.write(i2+ "effect.reparentTo(render)\n")
thenode=AllScene.particleNodes[effect]
out_file.write(i2+ "self.particleDict[\"" + effectS + "\"].setPosHprScale(%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f)\n"%(thenode.getX(),thenode.getY(),thenode.getZ(),thenode.getH(),thenode.getP(),thenode.getR(),thenode.getSx(),thenode.getSy(),thenode.getSz()))
out_file.write("\n")
out_file.write(i2+"return\n")
out_file.write("\n")
out_file.write(i1+"def enableeffect(self,effect_name):\n")
out_file.write(i2+"self.particleDict[effect_name].enable()\n")
out_file.write(i2+"return\n")
out_file.write("\n")
out_file.write(i1+"def disableeffect(self,effect_name):\n")
out_file.write(i2+"self.particleDict[effect_name].disable()\n")
out_file.write(i2+"return\n")
out_file.write("\n")
####################################################################################################################################################
# Animation Blending Methods
####################################################################################################################################################
out_file.write("\n")
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Animation Blending\n")
out_file.write(i2+"# Using blending:\n")
out_file.write(i2+"# theScene.playBlendAnim(actor,blendname)\n")
out_file.write(i2+"# theScene.stopBlendAnim(actor,blendname)\n")
out_file.write(i2+"# theScene.changeBlendAnim(actor,blendname,blend_amount)\n")
out_file.write(i2+"##########################################################################################################\n\n")
out_file.write(i1+"def playBlendAnim(self,actor,blendName,loop=0):\n")
out_file.write(i2+"actor.enableBlend()\n")
out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n")
out_file.write(i2+"blendList=blendDicts[blendName]\n")
out_file.write(i2+"actor.setControlEffect(blendList[0],blendList[2])\n")
out_file.write(i2+"actor.setControlEffect(blendList[1],1.0-blendList[2])\n")
out_file.write(i2+"if(loop):\n")
out_file.write(i2+i1+"actor.loop(blendList[0])\n")
out_file.write(i2+i1+"actor.loop(blendList[1])\n")
out_file.write(i2+"else:\n")
out_file.write(i2+i1+"actor.start(blendList[0])\n")
out_file.write(i2+i1+"actor.start(blendList[1])\n")
out_file.write("\n")
out_file.write(i1+"def stopBlendAnim(self,actor,blendName):\n")
out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n")
out_file.write(i2+"blendList=blendDicts[blendName]\n")
out_file.write(i2+"actor.stop(blendList[0])\n")
out_file.write(i2+"actor.stop(blendList[1])\n")
out_file.write("\n")
out_file.write(i1+"def changeBlending(self,actor,blendName,blending):\n")
out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n")
out_file.write(i2+"blendList=blendDicts[blendName]\n")
out_file.write(i2+"blendList[2]=blending\n")
out_file.write(i2+"self.blendAnimDict[actor.getName()]={blendName:[blendList[0],blendList[1],blending]}\n")
out_file.write("\n")
####################################################################################################################################################
# Hide and Show Methods
####################################################################################################################################################
out_file.write("\n")
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Hide and Show Methods\n")
out_file.write(i2+"# These will help you hide/show dummies, collision solids, effect nodes etc.\n")
out_file.write(i2+"##########################################################################################################\n\n")
out_file.write("\n")
out_file.write(i1+"def hideDummies(self):\n")
out_file.write("\n")
out_file.write(i2+"for dummy in self.dummyDict:\n")
out_file.write(i2+i1+"self.dummyDict[dummy].reparentTo(hidden)\n")
out_file.write("\n")
out_file.write(i1+"def hideCollSolids(self):\n")
out_file.write("\n")
out_file.write(i2+"for collSolid in self.collisionDict:\n")
out_file.write(i2+i1+"self.collisionDict[collSolid].hide()\n")
out_file.write("\n")
out_file.write(i1+"def hideEffectNodes(self):\n")
out_file.write("\n")
out_file.write(i2+"for effectnode in self.particleNodes:\n")
out_file.write(i2+i1+"self.particleNodes[effectnode].hide()\n")
out_file.write("\n")
out_file.write(i1+"def showDummies(self):\n")
out_file.write("\n")
out_file.write(i2+"for dummy in self.dummyDict:\n")
out_file.write(i2+i1+"self.dummyDict[dummy].reparentTo(hidden)\n")
out_file.write("\n")
out_file.write(i1+"def showCollSolids(self):\n")
out_file.write("\n")
out_file.write(i2+"for collSolid in self.collisionDict:\n")
out_file.write(i2+i1+"self.collisionDict[collSolid].show()\n")
out_file.write("\n")
out_file.write(i1+"def showEffectNodes(self):\n")
out_file.write("\n")
out_file.write(i2+"for effectnode in self.particleNodes:\n")
out_file.write(i2+i1+"self.particleNodes[effectnode].show()\n\n")
##########################################################################################################
# Saving Particle Parameters as a Class
##########################################################################################################
out_file.write("\n")
out_file.write(i2+"##########################################################################################################\n")
out_file.write(i2+"# Particle Effects\n")
out_file.write(i2+"# This is where effect parameters are saved in a class\n")
out_file.write(i2+"# The class is then instantiated in the starteffects method and appended to the dictionaries\n")
out_file.write(i2+"##########################################################################################################\n\n")
for effect in AllScene.particleDict:
out_file.write("\n\n")
out_file.write("class " + str(effect) + ":\n")
out_file.write(i1+"def __init__(self,mode=1,seParticleEffect=None,seParticles=None):\n")
out_file.write(i2+"if(mode==0):\n")
out_file.write(i2+i1+"self.effect=seParticleEffect.ParticleEffect()\n")
out_file.write(i2+"else:\n")
out_file.write(i2+i1+"self.effect=ParticleEffect.ParticleEffect()\n")
AllScene.particleDict[effect].AppendConfig(out_file)
#out_file.write(i2+"return self.effect\n")
out_file.write("\n\n")
out_file.write(i1+"def starteffect(self):\n")
out_file.write(i2+"pass\n")
out_file.write("\n\n")
out_file.write(i1+"def stopeffect(self):\n")
out_file.write(i2+"pass\n\n")
out_file.write(i1+"def getEffect(self):\n")
out_file.write(i2+"return self.effect\n\n")
#Un-comment the lines below to make this a stand-alone file
#out_file.write("Scene=SavedScene()\n")
#out_file.write("run()\n")
out_file.close()

View file

@ -0,0 +1,134 @@
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.showbase.PhysicsManagerGlobal import *
#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
from pandac.PandaModules import ForceNode
from direct.directnotify import DirectNotifyGlobal
import sys
class ForceGroup(DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory('ForceGroup')
id = 1
def __init__(self, name=None):
"""__init__(self)"""
if (name == None):
self.name = 'ForceGroup-%d' % ForceGroup.id
ForceGroup.id += 1
else:
self.name = name
self.node = ForceNode.ForceNode(self.name)
self.nodePath = NodePath(self.node)
self.fEnabled = 0
self.particleEffect = None
def cleanup(self):
self.node.clear()
self.nodePath.removeNode()
del self.nodePath
del self.node
del self.particleEffect
def enable(self):
"""
Convenience function to enable all forces in force group
"""
for i in range(self.node.getNumForces()):
f = self.node.getForce(i)
f.setActive(1)
self.fEnabled = 1
def disable(self):
"""
Convenience function to disable all forces in force group
"""
for i in range(self.node.getNumForces()):
f = self.node.getForce(i)
f.setActive(0)
self.fEnabled = 0
def isEnabled(self):
return self.fEnabled
def addForce(self, force):
"""addForce(self, force)"""
self.node.addForce(force)
if (self.particleEffect):
self.particleEffect.addForce(force)
def removeForce(self, force):
"""removeForce(self, force)"""
self.node.removeForce(force)
if (self.particleEffect != None):
self.particleEffect.removeForce(force)
# Get/set
def getName(self):
return self.name
def getNode(self):
return self.node
def getNodePath(self):
return self.nodePath
# Utility functions
def __getitem__(self, index):
numForces = self.node.getNumForces()
if ((index < 0) or (index >= numForces)):
raise IndexError
return self.node.getForce(index)
def __len__(self):
return self.node.getNumForces()
def asList(self):
l = []
for i in range(self.node.getNumForces()):
l.append(self.node.getForce(i))
return l
def printParams(self, file = sys.stdout, targ = 'self'):
i1=" "
i2=i1+i1
file.write(i2+'# Force parameters\n')
for i in range(self.node.getNumForces()):
f = self.node.getForce(i)
fname = 'force%d' % i
if isinstance(f, LinearForce):
amplitude = f.getAmplitude()
massDependent = f.getMassDependent()
if isinstance(f, LinearCylinderVortexForce):
file.write(i2+fname + ' = LinearCylinderVortexForce(%.4f, %.4f, %.4f, %.4f, %d)\n' % (f.getRadius(), f.getLength(), f.getCoef(), amplitude, massDependent))
elif isinstance(f, LinearDistanceForce):
radius = f.getRadius()
falloffType = f.getFalloffType()
ftype = 'FTONEOVERR'
if (falloffType == LinearDistanceForce.FTONEOVERR):
ftype = 'FTONEOVERR'
elif (falloffType == LinearDistanceForce.FTONEOVERRSQUARED):
ftype = 'FTONEOVERRSQUARED'
elif (falloffType == LinearDistanceForce.FTONEOVERRCUBED):
ftype = 'FTONEOVERRCUBED'
forceCenter = f.getForceCenter()
if isinstance(f, LinearSinkForce):
file.write(i2+fname + ' = LinearSinkForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
elif isinstance(f, LinearSourceForce):
file.write(i2+fname + ' = LinearSourceForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
elif isinstance(f, LinearFrictionForce):
file.write(i2+fname + ' = LinearFrictionForce(%.4f, %.4f, %d)\n' % (f.getCoef(), amplitude, massDependent))
elif isinstance(f, LinearJitterForce):
file.write(i2+fname + ' = LinearJitterForce(%.4f, %d)\n' % (amplitude, massDependent))
elif isinstance(f, LinearNoiseForce):
file.write(i2+fname + ' = LinearNoiseForce(%.4f, %d)\n' % (amplitude, massDependent))
elif isinstance(f, LinearVectorForce):
vec = f.getLocalVector()
file.write(i2+fname + ' = LinearVectorForce(Vec3(%.4f, %.4f, %.4f), %.4f, %d)\n' % (vec[0], vec[1], vec[2], amplitude, massDependent))
elif isinstance(f, AngularForce):
if isinstance(f, AngularVectorForce):
vec = f.getQuat()
file.write(i2+fname + ' = AngularVectorForce(Quat(%.4f, %.4f, %.4f))\n' % (vec[0], vec[1], vec[2], vec[3]))
file.write(i2+fname + '.setActive(%d)\n' % f.getActive())
file.write(i2+targ + '.addForce(%s)\n' % fname)

View file

@ -0,0 +1,256 @@
#################################################################
# seGeometry.py
# Originally from DirectGeometry.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We didn't change anything essential.
# Just because we customized the seSession from DirectSession,
# So we need related files can follow the change.
# However, we don't want to change anything inside the original directool
# to let them can work with our scene editor.
# (If we do change original directools, it will force user has to install the latest version of OUR Panda)
#
#################################################################
from pandac.PandaModules import *
from direct.directtools.DirectGlobals import *
from direct.directtools.DirectUtil import *
import math
class LineNodePath(NodePath):
def __init__(self, parent = None, name = None,
thickness = 1.0, colorVec = VBase4(1)):
# Initialize the superclass
NodePath.__init__(self)
if parent is None:
parent = hidden
# Attach a geomNode to the parent and set self to be
# the resulting node path
self.lineNode = GeomNode("lineNode")
self.assign(parent.attachNewNode( self.lineNode ))
if name:
self.setName(name)
# Create a lineSegs object to hold the line
ls = self.lineSegs = LineSegs()
# Initialize the lineSegs parameters
ls.setThickness(thickness)
ls.setColor(colorVec)
def moveTo( self, *_args ):
apply( self.lineSegs.moveTo, _args )
def drawTo( self, *_args ):
apply( self.lineSegs.drawTo, _args )
def create( self, frameAccurate = 0 ):
self.lineSegs.create( self.lineNode, frameAccurate )
def reset( self ):
self.lineSegs.reset()
self.lineNode.removeAllGeoms()
def isEmpty( self ):
return self.lineSegs.isEmpty()
def setThickness( self, thickness ):
self.lineSegs.setThickness( thickness )
def setColor( self, *_args ):
apply( self.lineSegs.setColor, _args )
def setVertex( self, *_args):
apply( self.lineSegs.setVertex, _args )
def setVertexColor( self, vertex, *_args ):
apply( self.lineSegs.setVertexColor, (vertex,) + _args )
def getCurrentPosition( self ):
return self.lineSegs.getCurrentPosition()
def getNumVertices( self ):
return self.lineSegs.getNumVertices()
def getVertex( self, index ):
return self.lineSegs.getVertex(index)
def getVertexColor( self ):
return self.lineSegs.getVertexColor()
def drawArrow(self, sv, ev, arrowAngle, arrowLength):
"""
Do the work of moving the cursor around to draw an arrow from
sv to ev. Hack: the arrows take the z value of the end point
"""
self.moveTo(sv)
self.drawTo(ev)
v = sv - ev
# Find the angle of the line
angle = math.atan2(v[1], v[0])
# Get the arrow angles
a1 = angle + deg2Rad(arrowAngle)
a2 = angle - deg2Rad(arrowAngle)
# Get the arrow points
a1x = arrowLength * math.cos(a1)
a1y = arrowLength * math.sin(a1)
a2x = arrowLength * math.cos(a2)
a2y = arrowLength * math.sin(a2)
z = ev[2]
self.moveTo(ev)
self.drawTo(Point3(ev + Point3(a1x, a1y, z)))
self.moveTo(ev)
self.drawTo(Point3(ev + Point3(a2x, a2y, z)))
def drawArrow2d(self, sv, ev, arrowAngle, arrowLength):
"""
Do the work of moving the cursor around to draw an arrow from
sv to ev. Hack: the arrows take the z value of the end point
"""
self.moveTo(sv)
self.drawTo(ev)
v = sv - ev
# Find the angle of the line
angle = math.atan2(v[2], v[0])
# Get the arrow angles
a1 = angle + deg2Rad(arrowAngle)
a2 = angle - deg2Rad(arrowAngle)
# Get the arrow points
a1x = arrowLength * math.cos(a1)
a1y = arrowLength * math.sin(a1)
a2x = arrowLength * math.cos(a2)
a2y = arrowLength * math.sin(a2)
self.moveTo(ev)
self.drawTo(Point3(ev + Point3(a1x, 0.0, a1y)))
self.moveTo(ev)
self.drawTo(Point3(ev + Point3(a2x, 0.0, a2y)))
def drawLines(self, lineList):
"""
Given a list of lists of points, draw a separate line for each list
"""
for pointList in lineList:
apply(self.moveTo, pointList[0])
for point in pointList[1:]:
apply(self.drawTo, point)
##
## Given a point in space, and a direction, find the point of intersection
## of that ray with a plane at the specified origin, with the specified normal
def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
t = 0
offset = planeOrigin - lineOrigin
t = offset.dot(normal) / lineDir.dot(normal)
hitPt = lineDir * t
return hitPt + lineOrigin
def getNearProjectionPoint(nodePath):
# Find the position of the projection of the specified node path
# on the near plane
origin = nodePath.getPos(SEditor.camera)
# project this onto near plane
if origin[1] != 0.0:
return origin * (SEditor.dr.near / origin[1])
else:
# Object is coplaner with camera, just return something reasonable
return Point3(0, SEditor.dr.near, 0)
def getScreenXY(nodePath):
# Where does the node path's projection fall on the near plane
nearVec = getNearProjectionPoint(nodePath)
# Clamp these coordinates to visible screen
nearX = CLAMP(nearVec[0], SEditor.dr.left, SEditor.dr.right)
nearY = CLAMP(nearVec[2], SEditor.dr.bottom, SEditor.dr.top)
# What percentage of the distance across the screen is this?
percentX = (nearX - SEditor.dr.left)/SEditor.dr.nearWidth
percentY = (nearY - SEditor.dr.bottom)/SEditor.dr.nearHeight
# Map this percentage to the same -1 to 1 space as the mouse
screenXY = Vec3((2 * percentX) - 1.0,nearVec[1],(2 * percentY) - 1.0)
# Return the resulting value
return screenXY
def getCrankAngle(center):
# Used to compute current angle of mouse (relative to the coa's
# origin) in screen space
x = SEditor.dr.mouseX - center[0]
y = SEditor.dr.mouseY - center[2]
return (180 + rad2Deg(math.atan2(y,x)))
def relHpr(nodePath, base, h, p, r):
# Compute nodePath2newNodePath relative to base coordinate system
# nodePath2base
mNodePath2Base = nodePath.getMat(base)
# delta scale, orientation, and position matrix
mBase2NewBase = Mat4()
composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h,p,r), ZERO_VEC,
CSDefault)
# base2nodePath
mBase2NodePath = base.getMat(nodePath)
# nodePath2 Parent
mNodePath2Parent = nodePath.getMat()
# Compose the result
resultMat = mNodePath2Base * mBase2NewBase
resultMat = resultMat * mBase2NodePath
resultMat = resultMat * mNodePath2Parent
# Extract and apply the hpr
hpr = Vec3(0)
decomposeMatrix(resultMat, VBase3(), hpr, VBase3(),
CSDefault)
nodePath.setHpr(hpr)
# Quaternion interpolation
def qSlerp(startQuat, endQuat, t):
startQ = Quat(startQuat)
destQuat = Quat.identQuat()
# Calc dot product
cosOmega = (startQ.getI() * endQuat.getI() +
startQ.getJ() * endQuat.getJ() +
startQ.getK() * endQuat.getK() +
startQ.getR() * endQuat.getR())
# If the above dot product is negative, it would be better to
# go between the negative of the initial and the final, so that
# we take the shorter path.
if ( cosOmega < 0.0 ):
cosOmega *= -1
startQ.setI(-1 * startQ.getI())
startQ.setJ(-1 * startQ.getJ())
startQ.setK(-1 * startQ.getK())
startQ.setR(-1 * startQ.getR())
if ((1.0 + cosOmega) > Q_EPSILON):
# usual case
if ((1.0 - cosOmega) > Q_EPSILON):
# usual case
omega = math.acos(cosOmega)
sinOmega = math.sin(omega)
startScale = math.sin((1.0 - t) * omega)/sinOmega
endScale = math.sin(t * omega)/sinOmega
else:
# ends very close
startScale = 1.0 - t
endScale = t
destQuat.setI(startScale * startQ.getI() +
endScale * endQuat.getI())
destQuat.setJ(startScale * startQ.getJ() +
endScale * endQuat.getJ())
destQuat.setK(startScale * startQ.getK() +
endScale * endQuat.getK())
destQuat.setR(startScale * startQ.getR() +
endScale * endQuat.getR())
else:
# ends nearly opposite
destQuat.setI(-startQ.getJ())
destQuat.setJ(startQ.getI())
destQuat.setK(-startQ.getR())
destQuat.setR(startQ.getK())
startScale = math.sin((0.5 - t) * math.pi)
endScale = math.sin(t * math.pi)
destQuat.setI(startScale * startQ.getI() +
endScale * endQuat.getI())
destQuat.setJ(startScale * startQ.getJ() +
endScale * endQuat.getJ())
destQuat.setK(startScale * startQ.getK() +
endScale * endQuat.getK())
return destQuat

View file

@ -0,0 +1,170 @@
#################################################################
# seGrid.py
# Originally from DirectGrid.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We didn't change anything essential.
# Just because we customized the seSession from DirectSession,
# So we need related files can follow the change.
# However, we don't want to change anything inside the original directool
# to let them can work with our scene editor.
# (If we do change original directools, it will force user has to install the latest version of OUR Panda)
#
#################################################################
from direct.showbase.DirectObject import *
from direct.directtools.DirectUtil import *
from seGeometry import *
class DirectGrid(NodePath,DirectObject):
def __init__(self):
# Initialize superclass
NodePath.__init__(self)
self.assign(hidden.attachNewNode('DirectGrid'))
# Don't wireframe or light
useDirectRenderStyle(self)
# Load up grid parts to initialize grid object
# Polygon used to mark grid plane
self.gridBack = loader.loadModel('models/misc/gridBack')
self.gridBack.reparentTo(self)
self.gridBack.setColor(0.5,0.5,0.5,0.5)
# Grid Lines
self.lines = self.attachNewNode('gridLines')
self.minorLines = LineNodePath(self.lines)
self.minorLines.lineNode.setName('minorLines')
self.minorLines.setColor(VBase4(0.3,0.55,1,1))
self.minorLines.setThickness(1)
self.majorLines = LineNodePath(self.lines)
self.majorLines.lineNode.setName('majorLines')
self.majorLines.setColor(VBase4(0.3,0.55,1,1))
self.majorLines.setThickness(5)
self.centerLines = LineNodePath(self.lines)
self.centerLines.lineNode.setName('centerLines')
self.centerLines.setColor(VBase4(1,0,0,0))
self.centerLines.setThickness(3)
# Small marker to hilight snap-to-grid point
self.snapMarker = loader.loadModel('models/misc/sphere')
self.snapMarker.node().setName('gridSnapMarker')
self.snapMarker.reparentTo(self)
self.snapMarker.setColor(1,0,0,1)
self.snapMarker.setScale(0.3)
self.snapPos = Point3(0)
# Initialize Grid characteristics
self.fXyzSnap = 1
self.fHprSnap = 1
self.gridSize = 100.0
self.gridSpacing = 5.0
self.snapAngle = 15.0
self.enable()
def enable(self):
self.reparentTo(SEditor.group)
self.updateGrid()
self.fEnabled = 1
def disable(self):
self.reparentTo(hidden)
self.fEnabled = 0
def toggleGrid(self):
if self.fEnabled:
self.disable()
else:
self.enable()
def isEnabled(self):
return self.fEnabled
def updateGrid(self):
# Update grid lines based upon current grid spacing and grid size
# First reset existing grid lines
self.minorLines.reset()
self.majorLines.reset()
self.centerLines.reset()
# Now redraw lines
numLines = int(math.ceil(self.gridSize/self.gridSpacing))
scaledSize = numLines * self.gridSpacing
center = self.centerLines
minor = self.minorLines
major = self.majorLines
for i in range(-numLines,numLines + 1):
if i == 0:
center.moveTo(i * self.gridSpacing, -scaledSize, 0)
center.drawTo(i * self.gridSpacing, scaledSize, 0)
center.moveTo(-scaledSize, i * self.gridSpacing, 0)
center.drawTo(scaledSize, i * self.gridSpacing, 0)
else:
if (i % 5) == 0:
major.moveTo(i * self.gridSpacing, -scaledSize, 0)
major.drawTo(i * self.gridSpacing, scaledSize, 0)
major.moveTo(-scaledSize, i * self.gridSpacing, 0)
major.drawTo(scaledSize, i * self.gridSpacing, 0)
else:
minor.moveTo(i * self.gridSpacing, -scaledSize, 0)
minor.drawTo(i * self.gridSpacing, scaledSize, 0)
minor.moveTo(-scaledSize, i * self.gridSpacing, 0)
minor.drawTo(scaledSize, i * self.gridSpacing, 0)
center.create()
minor.create()
major.create()
self.gridBack.setScale(scaledSize)
def setXyzSnap(self, fSnap):
self.fXyzSnap = fSnap
def getXyzSnap(self):
return self.fXyzSnap
def setHprSnap(self, fSnap):
self.fHprSnap = fSnap
def getHprSnap(self):
return self.fHprSnap
def computeSnapPoint(self, point):
# Start of with current point
self.snapPos.assign(point)
# Snap if necessary
if self.fXyzSnap:
self.snapPos.set(
ROUND_TO(self.snapPos[0], self.gridSpacing),
ROUND_TO(self.snapPos[1], self.gridSpacing),
ROUND_TO(self.snapPos[2], self.gridSpacing))
# Move snap marker to this point
self.snapMarker.setPos(self.snapPos)
# Return the hit point
return self.snapPos
def computeSnapAngle(self, angle):
return ROUND_TO(angle, self.snapAngle)
def setSnapAngle(self, angle):
self.snapAngle = angle
def getSnapAngle(self):
return self.snapAngle
def setGridSpacing(self, spacing):
self.gridSpacing = spacing
self.updateGrid()
def getGridSpacing(self):
return self.gridSpacing
def setGridSize(self, size):
# Set size of grid back and redraw lines
self.gridSize = size
self.updateGrid()
def getGridSize(self):
return self.gridSize

View file

@ -0,0 +1,633 @@
#################################################################
# seLights.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
from direct.showbase.DirectObject import *
from string import lower
from direct.directtools import DirectUtil
from pandac.PandaModules import *
import string
class seLight(NodePath):
#################################################################
# seLight(NodePath)
# This is an object for keeping light data and let we can manipulate
# lights as otherNopaths.
# This idea basically is from directLight.
# But the way directLight object worked is not we want in our
# sceneEditor. So, we wrote one by ourself.
#################################################################
def __init__(self, light, parent, type,
lightcolor=VBase4(0.3,0.3,0.3,1),
specularColor = VBase4(1),
position = Point3(0,0,0),
orientation = Vec3(1,0,0),
constant = 1.0,
linear = 0.0,
quadratic = 0.0,
exponent = 0.0,
tag="",
lence = None):
#################################################################
# __init__(self, light, parent, type,
# lightcolor=VBase4(0.3,0.3,0.3,1),
# specularColor = VBase4(1),
# position = Point3(0,0,0),
# orientation = Vec3(1,0,0),
# constant = 1.0,
# linear = 0.0,
# quadratic = 0.0,
# exponent = 0.0,
# tag="",
# lence = None):
# This constructor will create a light node inside it and upcast
# this light node to itself as a nodePath.
# Also, it will load a model as a reference to itself on the secene so that
# user can easily recognize where is the light and can easily manipulate
# by mouse picking and widget control.
#################################################################
# Initialize the super class
NodePath.__init__(self)
# Initialize and save values
self.light = light
self.type = type
self.lightcolor=lightcolor
self.specularColor = specularColor
self.position = position
self.orientation = orientation
self.constant = constant
self.linear = linear
self.quadratic = quadratic
self.exponent = exponent
self.lence = lence
self.active = True
if isinstance(light, Spotlight):
node = light.upcastToLensNode()
else:
node = light.upcastToPandaNode()
# Attach node to self
self.LightNode=parent.attachNewNode(node)
self.LightNode.setTag("Metadata",tag)
if(self.type=='spot'):
self.LightNode.setHpr(self.orientation)
self.LightNode.setPos(self.position)
else:
self.LightNode.setHpr(self.orientation)
self.LightNode.setPos(self.position)
self.assign(self.LightNode)
if(self.type=='spot'):
self.helpModel = loader.loadModel( "models/misc/Spotlight" )
elif(self.type=='point'):
self.helpModel = loader.loadModel( "models/misc/Pointlight" )
elif(self.type=='directional'):
self.helpModel = loader.loadModel( "models/misc/Dirlight" )
else:
self.helpModel = loader.loadModel( "models/misc/sphere" )
self.helpModel.setColor(self.lightcolor)
self.helpModel.reparentTo(self)
DirectUtil.useDirectRenderStyle(self.helpModel)
if not ((self.type == 'directional')or(self.type == 'point')or(self.type == 'spot')):
self.helpModel.hide()
def getLight(self):
#################################################################
# getLight(self)
# This function will return the light object it contains.
#################################################################
return self.light
def getLightColor(self):
#################################################################
# getLightColor(self)
# This function will return the color of the light color of this light node.
#################################################################
return self.lightcolor
def getName(self):
#################################################################
# getName(self)
# This function will return the name of this light.
#################################################################
return self.light.getName()
def rename(self,name):
#################################################################
# rename(self, name)
# User should specify a string object, name, as the new name of this
# light node. This function will rename itself and light object
# to this new name.
#################################################################
self.light.setName(name)
self.setName(name)
def getType(self):
#################################################################
# getType(self)
# This function will return a string which is the type of this
# light node. (We only have four types of light)
#################################################################
return self.type
def setColor(self,color):
#################################################################
# setColor(self, color)
# This function takes one input parameter, color, which is a
# VBase4 object. This function will simply set this object into
# light node it has to change the property of light.
# Also, if the light type is either "directional" or "point", which
# means it has a reference model with it, this function will also
# change the reference model's color to input value.
#################################################################
self.light.setColor(color)
self.lightcolor = color
if (self.type == 'directional')or(self.type == 'point'):
self.helpModel.setColor(self.lightcolor)
return
def getSpecColor(self):
#################################################################
# getSpecColor(self)
# This function will return the specular color of the light.
# Although you can call this function for all kinds of light,
# it will only meanful if this light is not a ambient light.
#################################################################
return self.specularColor
def setSpecColor(self,color):
#################################################################
# setSpecColor(self, color)
# This function can be used to set the specular color of the light.
# Although you can call this function for all kinds of light,
# it will only meanful if the light type is not "ambient"
#################################################################
self.light.setSpecularColor(color)
self.specularcolor = color
return
def getPosition(self):
#################################################################
# getPosition(self)
# getPosition(self)
# This functioln will return a Point3 object which contains
# the x, y, z position data of this light node.
# It only has meaning for "point Light" and "Directional light"
#################################################################
self.position = self.LightNode.getPos()
return self.position
def setPosition(self, pos):
#################################################################
# setPosition(self, pos)
# This function will take a Point3 object as a input.
# Then, this function will set the itself andd light node to the
# target point.
#################################################################
self.LightNode.setPos(pos)
self.position = pos
return
def getOrientation(self):
#################################################################
# getOrientation(self)
# This function will return a Vec3-type object which contains the
# orientation data of this light node
#
# This function will only have meaning for point light and directional light.
#
#################################################################
self.orientation = self.LightNode.getHpr()
return self.orientation
def setOrientation(self,orient):
#################################################################
# setOrientation(self, orient)
# This funtction will take a Vec3-type object as an input.
# Then this function will set itself nad light node to face
# the target orientation.
#
# This function only has meaning for point light and directional light
# type of lights.
#
#################################################################
self.LightNode.setHpr(orient)
self.orientation = orient
return
def getAttenuation(self):
#################################################################
# getAttenuation(self)
# This function will return a Vec3 type of object which contains
# the constant, linear and quadratic attenuation for this light node.
#
# This function will only have meaning for point light and spot light
# tyoe of lights.
#
#################################################################
return Vec3(self.constant,self.linear,self.quadratic)
def setConstantAttenuation(self, value):
#################################################################
# setConstantAttenuation(self, value)
# This function will take a float number as an input.
# Then, this function will set the Constant Atenuation value
# to this number.
#################################################################
self.light.setAttenuation(Vec3(value, self.linear, self.quadratic))
self.constant = value
return
def setLinearAttenuation(self, value):
#################################################################
# setLinearAttenuation(self, value)
# This function will take a float number as an input.
# Then, this function will set the Linear Atenuation value
# to this number.
#################################################################
self.light.setAttenuation(Vec3(self.constant, value, self.quadratic))
self.linear = value
return
def setQuadraticAttenuation(self, value):
#################################################################
# setQuadraticAttenuation(self, value)
# This function will take a float number as an input.
# Then, this function will set the Quadratic Atenuation value
# to this number.
#################################################################
self.light.setAttenuation(Vec3(self.constant, self.linear, value))
self.quadratic = value
return
def getExponent(self):
#################################################################
# getExponent(self)
# This function will return the value of the Exponent Attenuation
# of this light node. (float)
#################################################################
return self.exponent
def setExponent(self, value):
#################################################################
# setExponent(self, value)
# This function will take a float number as an input.
# Then, this function will set the Exponent Atenuation value
# to this number.
#################################################################
self.light.setExponent(value)
self.exponent = value
return
class seLightManager(NodePath):
#################################################################
# seLightManager(NodePath)
# This is the class we used to control al lightings in our sceneEditor.
#################################################################
def __init__(self):
# Initialize the superclass
NodePath.__init__(self)
# Create a node for the lights
self.lnode=render.attachNewNode('Lights')
self.assign(self.lnode)
# Create a light attrib
self.lightAttrib = LightAttrib.makeAllOff()
self.lightDict = {}
self.ambientCount = 0
self.directionalCount = 0
self.pointCount = 0
self.spotCount = 0
# Originally, we don't do this load model thing.
# But the problem is, if we don't, then it will cause some
# Bounding calculation error...
self.helpModel = loader.loadModel( "models/misc/sphere" )
self.helpModel.reparentTo(self)
self.helpModel.hide()
def create(self, type = 'ambient',
lightcolor=VBase4(0.3,0.3,0.3,1),
specularColor = VBase4(1),
position = Point3(0,0,0),
orientation = Vec3(1,0,0),
constant = 1.0,
linear = 0.0,
quadratic = 0.0,
exponent = 0.0,
tag= "",
name='DEFAULT_NAME'):
#################################################################
# create(self, type = 'ambient',
# lightcolor=VBase4(0.3,0.3,0.3,1),
# specularColor = VBase4(1),
# position = Point3(0,0,0),
# orientation = Vec3(1,0,0),
# constant = 1.0,
# linear = 0.0,
# quadratic = 0.0,
# exponent = 0.0,
# tag= "",
# name='DEFAULT_NAME')
# As you can see, once user call this function and specify those
# variables, this function will create a seLight node.
# In the default, the light which just has been created will be
# set to off.
#################################################################
### create the light
lence = None
if type == 'ambient':
self.ambientCount += 1
if(name=='DEFAULT_NAME'):
light = AmbientLight('ambient_' + `self.ambientCount`)
else:
light = AmbientLight(name)
light.setColor(lightcolor)
elif type == 'directional':
self.directionalCount += 1
if(name=='DEFAULT_NAME'):
light = DirectionalLight('directional_' + `self.directionalCount`)
else:
light = DirectionalLight(name)
light.setColor(lightcolor)
light.setSpecularColor(specularColor)
elif type == 'point':
self.pointCount += 1
if(name=='DEFAULT_NAME'):
light = PointLight('point_' + `self.pointCount`)
else:
light = PointLight(name)
light.setColor(lightcolor)
light.setSpecularColor(specularColor)
light.setAttenuation(Vec3(constant, linear, quadratic))
elif type == 'spot':
self.spotCount += 1
if(name=='DEFAULT_NAME'):
light = Spotlight('spot_' + `self.spotCount`)
else:
light = Spotlight(name)
light.setColor(lightcolor)
lence = PerspectiveLens()
light.setLens(lence)
light.setSpecularColor(specularColor)
light.setAttenuation(Vec3(constant, linear, quadratic))
light.setExponent(exponent)
else:
print 'Invalid light type'
return None
# Create the seLight objects and put the light object we just created into it.
lightNode = seLight(light,self,type,
lightcolor=lightcolor,
specularColor = specularColor,
position = position,
orientation = orientation,
constant = constant,
linear = linear,
quadratic = quadratic,
exponent = exponent,
tag=tag,
lence = lence
)
self.lightDict[light.getName()] = lightNode
self.setOn(lightNode)
return self.lightDict.keys(),lightNode
def addLight(self, light):
#################################################################
# addLight(self, light)
# This function will put light in and save its properties in to a seLight Node
# Attention!!
# only Spotlight obj nneds to be specified a lens node first. i.e. setLens() first!
#################################################################
type = lower(light.getType().getName())
light.upcastToNamable()
specularColor = VBase4(1)
position = Point3(0,0,0)
orientation = Vec3(1,0,0)
constant = 1.0
linear = 0.0
quadratic = 0.0
exponent = 0.0
lence = None
lightcolor = light.getColor()
if type == 'ambientlight':
type = 'ambient'
self.ambientCount += 1
elif type == 'directionallight':
type = 'directional'
self.directionalCount += 1
orientation = light.getDirection()
position = light.getPoint()
specularColor = light.getSpecularColor()
elif type == 'pointlight':
type = 'point'
self.pointCount += 1
position = light.getPoint()
specularColor = light.getSpecularColor()
Attenuation = light.getAttenuation()
constant = Attenuation.getX()
linear = Attenuation.getY()
quadratic = Attenuation.getZ()
elif type == 'spotlight':
type = 'spot'
self.spotCount += 1
specularColor = light.getSpecularColor()
Attenuation = light.getAttenuation()
constant = Attenuation.getX()
linear = Attenuation.getY()
quadratic = Attenuation.getZ()
exponent = light.getExponent()
else:
print 'Invalid light type'
return None
lightNode = seLight(light,self,type,
lightcolor=lightcolor,
specularColor = specularColor,
position = position,
orientation = orientation,
constant = constant,
linear = linear,
quadratic = quadratic,
exponent = exponent,
lence = lence)
self.lightDict[light.getName()] = lightNode
self.setOn(lightNode)
return self.lightDict.keys(),lightNode
def delete(self, name, removeEntry = True):
#################################################################
# delete(self, name, removeEntry = True)
# This function will remove light node had the same name with user input.
# Also, you can specify the removeEntry to decide to remove the entry from the lightDict or not.
# Normaly, you alway want to remove the entry from the dictionary. Thsi is only used for "deleteAll" function.
#################################################################
type = self.lightDict[name].getType()
if type == 'ambient':
self.ambientCount -= 1
elif type == 'directional':
self.directionalCount -= 1
elif type == 'point':
self.pointCount -= 1
elif type == 'spot':
self.spotCount -= 1
self.setOff(self.lightDict[name])
self.lightDict[name].removeChildren()
self.lightDict[name].removeNode()
if removeEntry:
del self.lightDict[name]
return self.lightDict.keys()
def deleteAll(self):
#################################################################
# deleteAll(self)
# This function will keep calling delete and put exist lights in
# until all lights have been eliminated.
#################################################################
for name in self.lightDict:
self.delete(name, removeEntry = False)
self.lightDict.clear()
def isLight(self,name):
#################################################################
# isLight(self.name)
# Use a string as a index to check if there existing a light named "name"
#################################################################
return self.lightDict.has_key(name)
def rename(self,oName,nName):
#################################################################
# rename(self, oName, nName)
# This function will reanem the light named "oName(String)" to
# nName(String)
#################################################################
if self.isLight(oName):
lightNode = self.lightDict[oName]
self.lightDict[nName] = lightNode
lightNode.rename(nName)
del self.lightDict[oName]
return self.lightDict.keys(),lightNode
else:
print '----Light Mnager: No such Light!'
def getLightNodeList(self):
#################################################################
# getLightNodeList(self)
# Return a list which contains all seLight nodes
#################################################################
list = []
for name in self.lightDict:
list.append(self.lightDict[name])
return list
def getLightNodeDict(self):
#################################################################
# getLightNodeDict(self)
# Return the light dictionary itself.
#
# Attention!
# Because it doesn't make a copy when you return a dictionary, it
# means when you can change the value from outside and that change
# will directly reflect back to here.
#
#################################################################
return self.lightDict
def getLightList(self):
#################################################################
# getLightList(self)
# Return a list which contains names of all lights.
#################################################################
list = []
for name in self.lightDict:
list.append(name)
return list
def getLightNode(self,lightName):
#################################################################
# getLightNode(self, lightName)
# This function will return a seLight Node using a string, lightName. as a index.
#################################################################
if lightName in self.lightDict:
return self.lightDict[lightName]
def allOn(self):
#################################################################
# allOb(self)
# Enable the lighting system.
#################################################################
# Turn on all lighting
render.node().setAttrib(self.lightAttrib)
# Make sure there is a default material
render.setMaterial(Material())
def allOff(self):
#################################################################
# allOff(self)
# Disable whole lighting system
#################################################################
# Turn off all lighting
render.node().clearAttrib(LightAttrib.getClassType())
def toggle(self):
#################################################################
# toggle(self)
# Toggles light attribute, but doesn't toggle individual lights
#################################################################
if render.node().hasAttrib(LightAttrib.getClassType()):
self.allOff()
else:
self.allOn()
def setOn(self, lightNode):
#################################################################
# setOn(lightNode)
# This function will enable the input seLight node.
# If the light system itself is down, activate it.
#################################################################
self.lightAttrib = self.lightAttrib.addLight(lightNode.getLight())
lightNode.active = True
if render.node().hasAttrib(LightAttrib.getClassType()):
render.node().setAttrib(self.lightAttrib)
def setOff(self, lightNode):
#################################################################
# setOff(self, lightNode)
# This function will disable the input seLight node
# If the light system itself is down, activate it.
#################################################################
lightNode.active = False
self.lightAttrib = self.lightAttrib.removeLight(lightNode.getLight())
if render.node().hasAttrib(LightAttrib.getClassType()):
render.node().setAttrib(self.lightAttrib)
def getList(self):
#################################################################
# getList(self)
# This function actually has the same functionality with getLightList(),
# but this one should be more efficient.
#################################################################
return self.lightDict.keys()

View file

@ -0,0 +1,956 @@
#################################################################
# seManipulation.py
# Originally from DirectManipulation.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We didn't change anything essential.
# Just because we customized the seSession from DirectSession,
# So we need related files can follow the change.
# However, we don't want to change anything inside the original directool
# to let them can work with our scene editor.
# (If we do change original directools, it will force user has to install the latest version of OUR Panda)
#
#################################################################
from direct.showbase.DirectObject import *
from direct.directtools.DirectGlobals import *
from direct.directtools.DirectUtil import *
from seGeometry import *
from direct.task import Task
class DirectManipulationControl(DirectObject):
def __init__(self):
# Create the grid
self.objectHandles = ObjectHandles()
self.hitPt = Point3(0)
self.prevHit = Vec3(0)
self.rotationCenter = Point3(0)
self.initScaleMag = 1
self.manipRef = SEditor.group.attachNewNode('manipRef')
self.hitPtDist = 0
self.constraint = None
self.rotateAxis = 'x'
self.lastCrankAngle = 0
self.fSetCoa = 0
self.fHitInit = 1
self.fScaleInit = 1
self.fWidgetTop = 0
self.fFreeManip = 1
self.fScaling = 0
self.mode = None
self.actionEvents = [
['DIRECT-mouse1', self.manipulationStart],
['DIRECT-mouse1Up', self.manipulationStop],
['tab', self.toggleObjectHandlesMode],
['.', self.objectHandles.multiplyScalingFactorBy, 2.0],
['>', self.objectHandles.multiplyScalingFactorBy, 2.0],
[',', self.objectHandles.multiplyScalingFactorBy, 0.5],
['<', self.objectHandles.multiplyScalingFactorBy, 0.5],
['shift-f', self.objectHandles.growToFit],
['i', self.plantSelectedNodePath],
]
def manipulationStart(self, modifiers):
# Start out in select mode
self.mode = 'select'
# Check for a widget hit point
entry = SEditor.iRay.pickWidget()
# Did we hit a widget?
if entry:
# Yes!
self.hitPt.assign(entry.getSurfacePoint(entry.getFromNodePath()))
self.hitPtDist = Vec3(self.hitPt).length()
# Constraint determined by nodes name
self.constraint = entry.getIntoNodePath().getName()
else:
# Nope, off the widget, no constraint
self.constraint = None
# Check to see if we are moving the object
# We are moving the object if we either wait long enough
taskMgr.doMethodLater(MANIPULATION_MOVE_DELAY,
self.switchToMoveMode,
'manip-move-wait')
# Or we move far enough
self.moveDir = None
watchMouseTask = Task.Task(self.watchMouseTask)
watchMouseTask.initX = SEditor.dr.mouseX
watchMouseTask.initY = SEditor.dr.mouseY
taskMgr.add(watchMouseTask, 'manip-watch-mouse')
def switchToMoveMode(self, state):
taskMgr.remove('manip-watch-mouse')
self.mode = 'move'
self.manipulateObject()
return Task.done
def watchMouseTask(self, state):
if (((abs (state.initX - SEditor.dr.mouseX)) > 0.01) or
((abs (state.initY - SEditor.dr.mouseY)) > 0.01)):
taskMgr.remove('manip-move-wait')
self.mode = 'move'
self.manipulateObject()
return Task.done
else:
return Task.cont
def manipulationStop(self,xy=[]):
taskMgr.remove('manipulateObject')
taskMgr.remove('manip-move-wait')
taskMgr.remove('manip-watch-mouse')
# depending on flag.....
if self.mode == 'select':
# Check for object under mouse
# Don't intersect with hidden or backfacing objects
skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
# Skip camera (and its children), unless control key is pressed
skipFlags |= SKIP_CAMERA * (1 - base.getControl())
entry = SEditor.iRay.pickGeom(skipFlags = skipFlags)
if entry:
# Record hit point information
self.hitPt.assign(entry.getSurfacePoint(entry.getFromNodePath()))
self.hitPtDist = Vec3(self.hitPt).length()
# Select it
SEditor.select(entry.getIntoNodePath(), SEditor.fShift)
else:
SEditor.deselectAll()
else:
self.manipulateObjectCleanup()
def manipulateObjectCleanup(self):
if self.fScaling:
# We had been scaling, need to reset object handles
self.objectHandles.transferObjectHandlesScale()
self.fScaling = 0
SEditor.selected.highlightAll()
self.objectHandles.showAllHandles()
self.objectHandles.hideGuides()
# Restart followSelectedNodePath task
self.spawnFollowSelectedNodePathTask()
messenger.send('DIRECT_manipulateObjectCleanup')
def spawnFollowSelectedNodePathTask(self):
# If nothing selected, just return
if not SEditor.selected.last:
return
# Clear out old task to make sure
taskMgr.remove('followSelectedNodePath')
# Where are the object handles relative to the selected object
pos = VBase3(0)
hpr = VBase3(0)
decomposeMatrix(SEditor.selected.last.mCoa2Dnp,
VBase3(0), hpr, pos, CSDefault)
# Create the task
t = Task.Task(self.followSelectedNodePathTask)
# Update state variables
t.pos = pos
t.hpr = hpr
t.base = SEditor.selected.last
# Spawn the task
taskMgr.add(t, 'followSelectedNodePath')
def followSelectedNodePathTask(self, state):
SEditor.widget.setPosHpr(state.base, state.pos, state.hpr)
return Task.cont
def enableManipulation(self):
# Accept events
for event in self.actionEvents:
self.accept(event[0], event[1], extraArgs = event[2:])
def disableManipulation(self):
# Ignore events
for event in self.actionEvents:
self.ignore(event[0])
self.removeManipulateObjectTask()
taskMgr.remove('manipulateObject')
taskMgr.remove('manip-move-wait')
taskMgr.remove('manip-watch-mouse')
taskMgr.remove('highlightWidgetTask')
taskMgr.remove('resizeObjectHandles')
def toggleObjectHandlesMode(self):
self.fSetCoa = 1 - self.fSetCoa
if self.fSetCoa:
self.objectHandles.coaModeColor()
else:
self.objectHandles.manipModeColor()
def removeManipulateObjectTask(self):
taskMgr.remove('manipulateObject')
def manipulateObject(self):
# Only do this if something is selected
if SEditor.selected:
# Remove the task to keep the widget attached to the object
taskMgr.remove('followSelectedNodePath')
# and the task to highlight the widget
taskMgr.remove('highlightWidgetTask')
# Set manipulation flag
self.fManip = 1
# Record undo point
SEditor.pushUndo(SEditor.selected)
# Update object handles visibility
self.objectHandles.showGuides()
self.objectHandles.hideAllHandles()
self.objectHandles.showHandle(self.constraint)
# Record relationship between selected nodes and widget
SEditor.selected.getWrtAll()
# hide the bbox of the selected objects during interaction
SEditor.selected.dehighlightAll()
# Send event to signal start of manipulation
messenger.send('DIRECT_manipulateObjectStart')
# Manipulate the real object with the constraint
# The constraint is passed as the name of the node
self.spawnManipulateObjectTask()
def spawnManipulateObjectTask(self):
# reset hit-pt flag
self.fHitInit = 1
self.fScaleInit = 1
# record initial offset between widget and camera
t = Task.Task(self.manipulateObjectTask)
t.fMouseX = abs(SEditor.dr.mouseX) > 0.9
t.fMouseY = abs(SEditor.dr.mouseY) > 0.9
if t.fMouseX:
t.constrainedDir = 'y'
else:
t.constrainedDir = 'x'
# Compute widget's xy coords in screen space
t.coaCenter = getScreenXY(SEditor.widget)
# These are used to rotate about view vector
if t.fMouseX and t.fMouseY:
t.lastAngle = getCrankAngle(t.coaCenter)
taskMgr.add(t, 'manipulateObject')
def manipulateObjectTask(self, state):
# Widget takes precedence
if self.constraint:
type = self.constraint[2:]
if type == 'post':
self.xlate1D(state)
elif type == 'disc':
self.xlate2D(state)
elif type == 'ring':
self.rotate1D(state)
# No widget interaction, determine free manip mode
elif self.fFreeManip:
# If we've been scaling and changed modes, reset object handles
if 0 and self.fScaling and (not SEditor.fAlt):
self.objectHandles.transferObjectHandlesScale()
self.fScaling = 0
# Alt key switches to a scaling mode
if SEditor.fControl:
self.fScaling = 1
self.scale3D(state)
# Otherwise, manip mode depends on where you started
elif state.fMouseX and state.fMouseY:
# In the corner, spin around camera's axis
self.rotateAboutViewVector(state)
elif state.fMouseX or state.fMouseY:
# Mouse started elsewhere in the outer frame, rotate
self.rotate2D(state)
else:
# Mouse started in central region, xlate
# Mode depends on shift key
if SEditor.fShift or SEditor.fControl:
self.xlateCamXY(state)
else:
self.xlateCamXZ(state)
if self.fSetCoa:
# Update coa based on current widget position
SEditor.selected.last.mCoa2Dnp.assign(
SEditor.widget.getMat(SEditor.selected.last))
else:
# Move the objects with the widget
SEditor.selected.moveWrtWidgetAll()
# Continue
return Task.cont
### WIDGET MANIPULATION METHODS ###
def xlate1D(self, state):
# Constrained 1D Translation along widget axis
# Compute nearest hit point along axis and try to keep
# that point as close to the current mouse position as possible
# what point on the axis is the mouse pointing at?
self.hitPt.assign(self.objectHandles.getAxisIntersectPt(
self.constraint[:1]))
# use it to see how far to move the widget
if self.fHitInit:
# First time through, just record that point
self.fHitInit = 0
self.prevHit.assign(self.hitPt)
else:
# Move widget to keep hit point as close to mouse as possible
offset = self.hitPt - self.prevHit
SEditor.widget.setPos(SEditor.widget, offset)
def xlate2D(self, state):
# Constrained 2D (planar) translation
# Compute point of intersection of ray from eyepoint through cursor
# to one of the three orthogonal planes on the widget.
# This point tracks all subsequent mouse movements
self.hitPt.assign(self.objectHandles.getWidgetIntersectPt(
SEditor.widget, self.constraint[:1]))
# use it to see how far to move the widget
if self.fHitInit:
# First time through just record hit point
self.fHitInit = 0
self.prevHit.assign(self.hitPt)
else:
offset = self.hitPt - self.prevHit
SEditor.widget.setPos(SEditor.widget, offset)
def rotate1D(self, state):
# Constrained 1D rotation about the widget's main axis (X,Y, or Z)
# Rotation depends upon circular motion of the mouse about the
# projection of the widget's origin on the image plane
# A complete circle about the widget results in a change in
# orientation of 360 degrees.
# First initialize hit point/rotation angle
if self.fHitInit:
self.fHitInit = 0
self.rotateAxis = self.constraint[:1]
self.fWidgetTop = self.widgetCheck('top?')
self.rotationCenter = getScreenXY(SEditor.widget)
self.lastCrankAngle = getCrankAngle(self.rotationCenter)
# Rotate widget based on how far cursor has swung around origin
newAngle = getCrankAngle(self.rotationCenter)
deltaAngle = self.lastCrankAngle - newAngle
if self.fWidgetTop:
deltaAngle = -1 * deltaAngle
if self.rotateAxis == 'x':
SEditor.widget.setP(SEditor.widget, deltaAngle)
elif self.rotateAxis == 'y':
if base.config.GetBool('temp-hpr-fix',0):
SEditor.widget.setR(SEditor.widget, deltaAngle)
else:
SEditor.widget.setR(SEditor.widget, -deltaAngle)
elif self.rotateAxis == 'z':
SEditor.widget.setH(SEditor.widget, deltaAngle)
# Record crank angle for next time around
self.lastCrankAngle = newAngle
def widgetCheck(self,type):
# Utility to see if we are looking at the top or bottom of
# a 2D planar widget or if we are looking at a 2D planar widget
# edge on
# Based upon angle between view vector from eye through the
# widget's origin and one of the three principle axes
axis = self.constraint[:1]
# First compute vector from eye through widget origin
mWidget2Cam = SEditor.widget.getMat(SEditor.camera)
# And determine where the viewpoint is relative to widget
pos = VBase3(0)
decomposeMatrix(mWidget2Cam, VBase3(0), VBase3(0), pos,
CSDefault)
widgetDir = Vec3(pos)
widgetDir.normalize()
# Convert specified widget axis to view space
if axis == 'x':
widgetAxis = Vec3(mWidget2Cam.xformVec(X_AXIS))
elif axis == 'y':
widgetAxis = Vec3(mWidget2Cam.xformVec(Y_AXIS))
elif axis == 'z':
widgetAxis = Vec3(mWidget2Cam.xformVec(Z_AXIS))
widgetAxis.normalize()
if type == 'top?':
# Check sign of angle between two vectors
return (widgetDir.dot(widgetAxis) < 0.)
elif type == 'edge?':
# Checking to see if we are viewing edge-on
# Check angle between two vectors
return(abs(widgetDir.dot(widgetAxis)) < .2)
### FREE MANIPULATION METHODS ###
def xlateCamXZ(self, state):
"""Constrained 2D motion parallel to the camera's image plane
This moves the object in the camera's XZ plane"""
# reset fHitInit in case we later switch to manip mode
self.fHitInit = 1
# Reset scaling init flag
self.fScaleInit = 1
# Where is the widget relative to current camera view
vWidget2Camera = SEditor.widget.getPos(SEditor.camera)
x = vWidget2Camera[0]
y = vWidget2Camera[1]
z = vWidget2Camera[2]
# Move widget (and objects) based upon mouse motion
# Scaled up accordingly based upon widget distance
dr = SEditor.dr
SEditor.widget.setX(
SEditor.camera,
x + 0.5 * dr.mouseDeltaX * dr.nearWidth * (y/dr.near))
SEditor.widget.setZ(
SEditor.camera,
z + 0.5 * dr.mouseDeltaY * dr.nearHeight * (y/dr.near))
def xlateCamXY(self, state):
"""Constrained 2D motion perpendicular to camera's image plane
This moves the object in the camera's XY plane if shift is held
Moves object toward camera if control is held
"""
# Reset scaling init flag
self.fScaleInit = 1
# Now, where is the widget relative to current camera view
vWidget2Camera = SEditor.widget.getPos(SEditor.camera)
# If this is first time around, record initial y distance
if self.fHitInit:
self.fHitInit = 0
# Use distance to widget to scale motion along Y
self.xlateSF = Vec3(vWidget2Camera).length()
# Get widget's current xy coords in screen space
coaCenter = getNearProjectionPoint(SEditor.widget)
self.deltaNearX = coaCenter[0] - SEditor.dr.nearVec[0]
# Which way do we move the object?
if SEditor.fControl:
moveDir = Vec3(vWidget2Camera)
# If widget is behind camera invert vector
if moveDir[1] < 0.0:
moveDir.assign(moveDir * -1)
moveDir.normalize()
else:
moveDir = Vec3(Y_AXIS)
# Move selected objects
dr = SEditor.dr
# Scale move dir
moveDir.assign(moveDir * (2.0 * dr.mouseDeltaY * self.xlateSF))
# Add it to current widget offset
vWidget2Camera += moveDir
# The object, however, stays at the same relative point to mouse in X
vWidget2Camera.setX((dr.nearVec[0] + self.deltaNearX) *
(vWidget2Camera[1]/dr.near))
# Move widget
SEditor.widget.setPos(SEditor.camera, vWidget2Camera)
def rotate2D(self, state):
""" Virtual trackball rotation of widget """
# Reset init flag in case we switch to another mode
self.fHitInit = 1
# Reset scaling init flag
self.fScaleInit = 1
tumbleRate = 360
# If moving outside of center, ignore motion perpendicular to edge
if ((state.constrainedDir == 'y') and (abs(SEditor.dr.mouseX) > 0.9)):
deltaX = 0
deltaY = SEditor.dr.mouseDeltaY
elif ((state.constrainedDir == 'x') and (abs(SEditor.dr.mouseY) > 0.9)):
deltaX = SEditor.dr.mouseDeltaX
deltaY = 0
else:
deltaX = SEditor.dr.mouseDeltaX
deltaY = SEditor.dr.mouseDeltaY
# Mouse motion edge to edge of display region results in one full turn
relHpr(SEditor.widget, SEditor.camera, deltaX * tumbleRate,
-deltaY * tumbleRate, 0)
def rotateAboutViewVector(self, state):
# Reset init flag in case we switch to another mode
self.fHitInit = 1
# Reset scaling init flag
self.fScaleInit = 1
# Compute current angle
angle = getCrankAngle(state.coaCenter)
deltaAngle = angle - state.lastAngle
state.lastAngle = angle
# Mouse motion edge to edge of display region results in one full turn
if base.config.GetBool('temp-hpr-fix',0):
relHpr(SEditor.widget, SEditor.camera, 0, 0, -deltaAngle)
else:
relHpr(SEditor.widget, SEditor.camera, 0, 0, deltaAngle)
def scale3D(self, state):
# Scale the selected node based upon up down mouse motion
# Mouse motion from edge to edge results in a factor of 4 scaling
# From midpoint to edge doubles or halves objects scale
if self.fScaleInit:
self.fScaleInit = 0
self.manipRef.setPos(SEditor.widget, 0, 0, 0)
self.manipRef.setHpr(SEditor.camera, 0, 0, 0)
self.initScaleMag = Vec3(
self.objectHandles.getWidgetIntersectPt(
self.manipRef, 'y')).length()
# record initial scale
self.initScale = SEditor.widget.getScale()
# Reset fHitInitFlag
self.fHitInit = 1
# Begin
# Scale factor is ratio current mag with init mag
currScale = (
self.initScale *
(self.objectHandles.getWidgetIntersectPt(
self.manipRef, 'y').length() /
self.initScaleMag)
)
SEditor.widget.setScale(currScale)
## Utility functions ##
def plantSelectedNodePath(self):
""" Move selected object to intersection point of cursor on scene """
# Check for intersection
entry = SEditor.iRay.pickGeom(
skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
# MRM: Need to handle moving COA
if (entry != None) and (SEditor.selected.last != None):
# Record undo point
SEditor.pushUndo(SEditor.selected)
# Record wrt matrix
SEditor.selected.getWrtAll()
# Move selected
SEditor.widget.setPos(
SEditor.camera,entry.getSurfacePoint(entry.getFromNodePath()))
# Move all the selected objects with widget
# Move the objects with the widget
SEditor.selected.moveWrtWidgetAll()
# Let everyone know that something was moved
messenger.send('DIRECT_manipulateObjectCleanup')
class ObjectHandles(NodePath,DirectObject):
def __init__(self):
# Initialize the superclass
NodePath.__init__(self)
# Load up object handles model and assign it to self
self.assign(loader.loadModel('models/misc/objectHandles'))
self.setName('objectHandles')
self.scalingNode = self.getChild(0)
self.scalingNode.setName('ohScalingNode')
self.ohScalingFactor = 1.0
# To avoid recreating a vec every frame
self.hitPt = Vec3(0)
# Get a handle on the components
self.xHandles = self.find('**/X')
self.xPostGroup = self.xHandles.find('**/x-post-group')
self.xPostCollision = self.xHandles.find('**/x-post')
self.xRingGroup = self.xHandles.find('**/x-ring-group')
self.xRingCollision = self.xHandles.find('**/x-ring')
self.xDiscGroup = self.xHandles.find('**/x-disc-group')
self.xDisc = self.xHandles.find('**/x-disc-visible')
self.xDiscCollision = self.xHandles.find('**/x-disc')
self.yHandles = self.find('**/Y')
self.yPostGroup = self.yHandles.find('**/y-post-group')
self.yPostCollision = self.yHandles.find('**/y-post')
self.yRingGroup = self.yHandles.find('**/y-ring-group')
self.yRingCollision = self.yHandles.find('**/y-ring')
self.yDiscGroup = self.yHandles.find('**/y-disc-group')
self.yDisc = self.yHandles.find('**/y-disc-visible')
self.yDiscCollision = self.yHandles.find('**/y-disc')
self.zHandles = self.find('**/Z')
self.zPostGroup = self.zHandles.find('**/z-post-group')
self.zPostCollision = self.zHandles.find('**/z-post')
self.zRingGroup = self.zHandles.find('**/z-ring-group')
self.zRingCollision = self.zHandles.find('**/z-ring')
self.zDiscGroup = self.zHandles.find('**/z-disc-group')
self.zDisc = self.zHandles.find('**/z-disc-visible')
self.zDiscCollision = self.zHandles.find('**/z-disc')
# Adjust visiblity, colors, and transparency
self.xPostCollision.hide()
self.xRingCollision.hide()
self.xDisc.setColor(1,0,0,.2)
self.yPostCollision.hide()
self.yRingCollision.hide()
self.yDisc.setColor(0,1,0,.2)
self.zPostCollision.hide()
self.zRingCollision.hide()
self.zDisc.setColor(0,0,1,.2)
# Augment geometry with lines
self.createObjectHandleLines()
# Create long markers to help line up in world
self.createGuideLines()
self.hideGuides()
# Start with widget handles hidden
self.fActive = 1
self.toggleWidget()
# Make sure object handles are never lit or drawn in wireframe
useDirectRenderStyle(self)
def coaModeColor(self):
self.setColor(.5,.5,.5,1)
def manipModeColor(self):
self.clearColor()
def toggleWidget(self):
if self.fActive:
self.deactivate()
else:
self.activate()
def activate(self):
self.scalingNode.reparentTo(self)
self.fActive = 1
def deactivate(self):
self.scalingNode.reparentTo(hidden)
self.fActive = 0
def showWidgetIfActive(self):
if self.fActive:
self.reparentTo(SEditor.group)
def showWidget(self):
self.reparentTo(SEditor.group)
def hideWidget(self):
self.reparentTo(hidden)
def enableHandles(self, handles):
if type(handles) == types.ListType:
for handle in handles:
self.enableHandle(handle)
elif handles == 'x':
self.enableHandles(['x-post','x-ring','x-disc'])
elif handles == 'y':
self.enableHandles(['y-post','y-ring','y-disc'])
elif handles == 'z':
self.enableHandles(['z-post','z-ring','z-disc'])
elif handles == 'post':
self.enableHandles(['x-post','y-post','z-post'])
elif handles == 'ring':
self.enableHandles(['x-ring','y-ring','z-ring'])
elif handles == 'disc':
self.enableHandles(['x-disc','y-disc','z-disc'])
elif handles == 'all':
self.enableHandles(['x-post','x-ring','x-disc',
'y-post','y-ring','y-disc',
'z-post','z-ring','z-disc'])
def enableHandle(self, handle):
if handle == 'x-post':
self.xPostGroup.reparentTo(self.xHandles)
elif handle == 'x-ring':
self.xRingGroup.reparentTo(self.xHandles)
elif handle == 'x-disc':
self.xDiscGroup.reparentTo(self.xHandles)
if handle == 'y-post':
self.yPostGroup.reparentTo(self.yHandles)
elif handle == 'y-ring':
self.yRingGroup.reparentTo(self.yHandles)
elif handle == 'y-disc':
self.yDiscGroup.reparentTo(self.yHandles)
if handle == 'z-post':
self.zPostGroup.reparentTo(self.zHandles)
elif handle == 'z-ring':
self.zRingGroup.reparentTo(self.zHandles)
elif handle == 'z-disc':
self.zDiscGroup.reparentTo(self.zHandles)
def disableHandles(self, handles):
if type(handles) == types.ListType:
for handle in handles:
self.disableHandle(handle)
elif handles == 'x':
self.disableHandles(['x-post','x-ring','x-disc'])
elif handles == 'y':
self.disableHandles(['y-post','y-ring','y-disc'])
elif handles == 'z':
self.disableHandles(['z-post','z-ring','z-disc'])
elif handles == 'post':
self.disableHandles(['x-post','y-post','z-post'])
elif handles == 'ring':
self.disableHandles(['x-ring','y-ring','z-ring'])
elif handles == 'disc':
self.disableHandles(['x-disc','y-disc','z-disc'])
elif handles == 'all':
self.disableHandles(['x-post','x-ring','x-disc',
'y-post','y-ring','y-disc',
'z-post','z-ring','z-disc'])
def disableHandle(self, handle):
if handle == 'x-post':
self.xPostGroup.reparentTo(hidden)
elif handle == 'x-ring':
self.xRingGroup.reparentTo(hidden)
elif handle == 'x-disc':
self.xDiscGroup.reparentTo(hidden)
if handle == 'y-post':
self.yPostGroup.reparentTo(hidden)
elif handle == 'y-ring':
self.yRingGroup.reparentTo(hidden)
elif handle == 'y-disc':
self.yDiscGroup.reparentTo(hidden)
if handle == 'z-post':
self.zPostGroup.reparentTo(hidden)
elif handle == 'z-ring':
self.zRingGroup.reparentTo(hidden)
elif handle == 'z-disc':
self.zDiscGroup.reparentTo(hidden)
def showAllHandles(self):
self.xPost.show()
self.xRing.show()
self.xDisc.show()
self.yPost.show()
self.yRing.show()
self.yDisc.show()
self.zPost.show()
self.zRing.show()
self.zDisc.show()
def hideAllHandles(self):
self.xPost.hide()
self.xRing.hide()
self.xDisc.hide()
self.yPost.hide()
self.yRing.hide()
self.yDisc.hide()
self.zPost.hide()
self.zRing.hide()
self.zDisc.hide()
def showHandle(self, handle):
if handle == 'x-post':
self.xPost.show()
elif handle == 'x-ring':
self.xRing.show()
elif handle == 'x-disc':
self.xDisc.show()
elif handle == 'y-post':
self.yPost.show()
elif handle == 'y-ring':
self.yRing.show()
elif handle == 'y-disc':
self.yDisc.show()
elif handle == 'z-post':
self.zPost.show()
elif handle == 'z-ring':
self.zRing.show()
elif handle == 'z-disc':
self.zDisc.show()
def showGuides(self):
self.guideLines.show()
def hideGuides(self):
self.guideLines.hide()
def setScalingFactor(self, scaleFactor):
self.ohScalingFactor = scaleFactor
self.scalingNode.setScale(self.ohScalingFactor)
def getScalingFactor(self):
return self.scalingNode.getScale()
def transferObjectHandlesScale(self):
# see how much object handles have been scaled
ohs = self.getScale()
sns = self.scalingNode.getScale()
# Transfer this to the scaling node
self.scalingNode.setScale(
ohs[0] * sns[0],
ohs[1] * sns[1],
ohs[2] * sns[2])
self.setScale(1)
def multiplyScalingFactorBy(self, factor):
taskMgr.remove('resizeObjectHandles')
sf = self.ohScalingFactor = self.ohScalingFactor * factor
self.scalingNode.lerpScale(sf,sf,sf, 0.5,
blendType = 'easeInOut',
task = 'resizeObjectHandles')
def growToFit(self):
taskMgr.remove('resizeObjectHandles')
# Increase handles scale until they cover 80% of the min dimension
# Originally, here is "cover 30% of the min dimension", we changed.
pos = SEditor.widget.getPos(SEditor.camera)
minDim = min(SEditor.dr.nearWidth, SEditor.dr.nearHeight)
sf = 0.4 * minDim * (pos[1]/SEditor.dr.near)
self.ohScalingFactor = sf
self.scalingNode.lerpScale(sf,sf,sf, 0.5,
blendType = 'easeInOut',
task = 'resizeObjectHandles')
def createObjectHandleLines(self):
# X post
self.xPost = self.xPostGroup.attachNewNode('x-post-visible')
lines = LineNodePath(self.xPost)
lines.setColor(VBase4(1,0,0,1))
lines.setThickness(5)
lines.moveTo(0,0,0)
lines.drawTo(1.5,0,0)
lines.create()
lines = LineNodePath(self.xPost)
lines.setColor(VBase4(1,0,0,1))
lines.setThickness(1.5)
lines.moveTo(0,0,0)
lines.drawTo(-1.5,0,0)
lines.create()
# X ring
self.xRing = self.xRingGroup.attachNewNode('x-ring-visible')
lines = LineNodePath(self.xRing)
lines.setColor(VBase4(1,0,0,1))
lines.setThickness(3)
lines.moveTo(0,1,0)
for ang in range(15, 370, 15):
lines.drawTo(0,
math.cos(deg2Rad(ang)),
math.sin(deg2Rad(ang)))
lines.create()
# Y post
self.yPost = self.yPostGroup.attachNewNode('y-post-visible')
lines = LineNodePath(self.yPost)
lines.setColor(VBase4(0,1,0,1))
lines.setThickness(5)
lines.moveTo(0,0,0)
lines.drawTo(0,1.5,0)
lines.create()
lines = LineNodePath(self.yPost)
lines.setColor(VBase4(0,1,0,1))
lines.setThickness(1.5)
lines.moveTo(0,0,0)
lines.drawTo(0,-1.5,0)
lines.create()
# Y ring
self.yRing = self.yRingGroup.attachNewNode('y-ring-visible')
lines = LineNodePath(self.yRing)
lines.setColor(VBase4(0,1,0,1))
lines.setThickness(3)
lines.moveTo(1,0,0)
for ang in range(15, 370, 15):
lines.drawTo(math.cos(deg2Rad(ang)),
0,
math.sin(deg2Rad(ang)))
lines.create()
# Z post
self.zPost = self.zPostGroup.attachNewNode('z-post-visible')
lines = LineNodePath(self.zPost)
lines.setColor(VBase4(0,0,1,1))
lines.setThickness(5)
lines.moveTo(0,0,0)
lines.drawTo(0,0,1.5)
lines.create()
lines = LineNodePath(self.zPost)
lines.setColor(VBase4(0,0,1,1))
lines.setThickness(1.5)
lines.moveTo(0,0,0)
lines.drawTo(0,0,-1.5)
lines.create()
# Z ring
self.zRing = self.zRingGroup.attachNewNode('z-ring-visible')
lines = LineNodePath(self.zRing)
lines.setColor(VBase4(0,0,1,1))
lines.setThickness(3)
lines.moveTo(1,0,0)
for ang in range(15, 370, 15):
lines.drawTo(math.cos(deg2Rad(ang)),
math.sin(deg2Rad(ang)),
0)
lines.create()
def createGuideLines(self):
self.guideLines = self.attachNewNode('guideLines')
# X guide lines
lines = LineNodePath(self.guideLines)
lines.setColor(VBase4(1,0,0,1))
lines.setThickness(0.5)
lines.moveTo(-500,0,0)
lines.drawTo(500,0,0)
lines.create()
lines.setName('x-guide')
# Y guide lines
lines = LineNodePath(self.guideLines)
lines.setColor(VBase4(0,1,0,1))
lines.setThickness(0.5)
lines.moveTo(0,-500,0)
lines.drawTo(0,500,0)
lines.create()
lines.setName('y-guide')
# Z guide lines
lines = LineNodePath(self.guideLines)
lines.setColor(VBase4(0,0,1,1))
lines.setThickness(0.5)
lines.moveTo(0,0,-500)
lines.drawTo(0,0,500)
lines.create()
lines.setName('z-guide')
def getAxisIntersectPt(self, axis):
# Calc the xfrom from camera to widget
mCam2Widget = SEditor.camera.getMat(SEditor.widget)
lineDir = Vec3(mCam2Widget.xformVec(SEditor.dr.nearVec))
lineDir.normalize()
# And determine where the viewpoint is relative to widget
lineOrigin = VBase3(0)
decomposeMatrix(mCam2Widget, VBase3(0), VBase3(0), lineOrigin,
CSDefault)
# Now see where this hits the plane containing the 1D motion axis.
# Pick the intersection plane most normal to the intersection ray
# by comparing lineDir with plane normals. The plane with the
# largest dotProduct is most "normal"
if axis == 'x':
if (abs(lineDir.dot(Y_AXIS)) > abs(lineDir.dot(Z_AXIS))):
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
else:
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS))
# We really only care about the nearest point on the axis
self.hitPt.setY(0)
self.hitPt.setZ(0)
elif axis == 'y':
if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Z_AXIS))):
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
else:
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS))
# We really only care about the nearest point on the axis
self.hitPt.setX(0)
self.hitPt.setZ(0)
elif axis == 'z':
if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Y_AXIS))):
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
else:
self.hitPt.assign(
planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
# We really only care about the nearest point on the axis
self.hitPt.setX(0)
self.hitPt.setY(0)
return self.hitPt
def getWidgetIntersectPt(self, nodePath, plane):
# Find out the point of interection of the ray passing though the mouse
# with the plane containing the 2D xlation or 1D rotation widgets
# Calc the xfrom from camera to the nodePath
mCam2NodePath = SEditor.camera.getMat(nodePath)
# And determine where the viewpoint is relative to widget
lineOrigin = VBase3(0)
decomposeMatrix(mCam2NodePath, VBase3(0), VBase3(0), lineOrigin,
CSDefault)
# Next we find the vector from viewpoint to the widget through
# the mouse's position on near plane.
# This defines the intersection ray
lineDir = Vec3(mCam2NodePath.xformVec(SEditor.dr.nearVec))
lineDir.normalize()
# Find the hit point
if plane == 'x':
self.hitPt.assign(planeIntersect(
lineOrigin, lineDir, ORIGIN, X_AXIS))
elif plane == 'y':
self.hitPt.assign(planeIntersect(
lineOrigin, lineDir, ORIGIN, Y_AXIS))
elif plane == 'z':
self.hitPt.assign(planeIntersect(
lineOrigin, lineDir, ORIGIN, Z_AXIS))
return self.hitPt

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,267 @@
from pandac.PandaModules import *
import seParticles
import seForceGroup
from direct.directnotify import DirectNotifyGlobal
class ParticleEffect(NodePath):
notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect')
pid = 1
def __init__(self, name=None, particles=None):
"""__init__()"""
if (name == None):
name = 'particle-effect-%d' % ParticleEffect.pid
ParticleEffect.pid += 1
NodePath.__init__(self, name)
# Record particle effect name
self.name = name
# Enabled flag
self.fEnabled = 0
# Dictionary of particles and forceGroups
self.particlesDict = {}
self.forceGroupDict = {}
# The effect's particle system
if (particles != None):
self.addParticles(particles)
self.renderParent = None
def start(self, parent=None, renderParent=None):
assert(self.notify.debug('start() - name: %s' % self.name))
self.renderParent = renderParent
self.enable()
if (parent != None):
self.reparentTo(parent)
def cleanup(self):
self.removeNode()
self.disable()
for f in self.forceGroupDict.values():
f.cleanup()
for p in self.particlesDict.values():
p.cleanup()
del self.renderParent
del self.particlesDict
del self.forceGroupDict
def reset(self):
self.removeAllForces()
self.removeAllParticles()
self.forceGroupDict = {}
self.particlesDict = {}
def enable(self):
"""enable()"""
if (self.renderParent != None):
for p in self.particlesDict.values():
p.setRenderParent(self.renderParent.node())
for f in self.forceGroupDict.values():
f.enable()
for p in self.particlesDict.values():
p.enable()
self.fEnabled = 1
def disable(self):
"""disable()"""
self.detachNode()
for p in self.particlesDict.values():
p.setRenderParent(p.node)
for f in self.forceGroupDict.values():
f.disable()
for p in self.particlesDict.values():
p.disable()
self.fEnabled = 0
def isEnabled(self):
"""
isEnabled()
Note: this may be misleading if enable(),disable() not used
"""
return self.fEnabled
def addForceGroup(self, forceGroup):
"""addForceGroup(forceGroup)"""
forceGroup.nodePath.reparentTo(self)
forceGroup.particleEffect = self
self.forceGroupDict[forceGroup.getName()] = forceGroup
# Associate the force group with all particles
for i in range(len(forceGroup)):
self.addForce(forceGroup[i])
def addForce(self, force):
"""addForce(force)"""
for p in self.particlesDict.values():
p.addForce(force)
def removeForceGroup(self, forceGroup):
"""removeForceGroup(forceGroup)"""
# Remove forces from all particles
for i in range(len(forceGroup)):
self.removeForce(forceGroup[i])
forceGroup.nodePath.removeNode()
forceGroup.particleEffect = None
del self.forceGroupDict[forceGroup.getName()]
def removeForce(self, force):
"""removeForce(force)"""
for p in self.particlesDict.values():
p.removeForce(force)
def removeAllForces(self):
for fg in self.forceGroupDict.values():
self.removeForceGroup(fg)
def addParticles(self, particles):
"""addParticles(particles)"""
particles.nodePath.reparentTo(self)
self.particlesDict[particles.getName()] = particles
# Associate all forces in all force groups with the particles
for fg in self.forceGroupDict.values():
for i in range(len(fg)):
particles.addForce(fg[i])
def removeParticles(self, particles):
"""removeParticles(particles)"""
if (particles == None):
self.notify.warning('removeParticles() - particles == None!')
return
particles.nodePath.detachNode()
del self.particlesDict[particles.getName()]
# Remove all forces from the particles
for fg in self.forceGroupDict.values():
for f in fg.asList():
particles.removeForce(f)
def removeAllParticles(self):
for p in self.particlesDict.values():
self.removeParticles(p)
def getParticlesList(self):
"""getParticles()"""
return self.particlesDict.values()
def getParticlesNamed(self, name):
"""getParticlesNamed(name)"""
return self.particlesDict.get(name, None)
def getParticlesDict(self):
"""getParticlesDict()"""
return self.particlesDict
def getForceGroupList(self):
"""getForceGroup()"""
return self.forceGroupDict.values()
def getForceGroupNamed(self, name):
"""getForceGroupNamed(name)"""
return self.forceGroupDict.get(name, None)
def getForceGroupDict(self):
"""getForceGroup()"""
return self.forceGroupDict
def saveConfig(self, filename):
"""saveFileData(filename)"""
f = open(filename.toOsSpecific(), 'wb')
# Add a blank line
f.write('\n')
# Make sure we start with a clean slate
f.write('self.reset()\n')
pos = self.getPos()
hpr = self.getHpr()
scale = self.getScale()
f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
(pos[0], pos[1], pos[2]))
f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
(hpr[0], hpr[1], hpr[2]))
f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
(scale[0], scale[1], scale[2]))
# Save all the particles to file
num = 0
for p in self.particlesDict.values():
target = 'p%d' % num
num = num + 1
f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
p.printParams(f, target)
f.write('self.addParticles(%s)\n' % target)
# Save all the forces to file
num = 0
for fg in self.forceGroupDict.values():
target = 'f%d' % num
num = num + 1
f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
fg.getName())
fg.printParams(f, target)
f.write('self.addForceGroup(%s)\n' % target)
# Close the file
f.close()
def loadConfig(self, filename):
"""loadConfig(filename)"""
#try:
# if vfs:
print vfs.readFile(filename)
exec vfs.readFile(filename)
print "Particle Effect Reading using VFS"
# else:
# execfile(filename.toOsSpecific())
# print "Shouldnt be wrong"
#except:
# self.notify.error('loadConfig: failed to load particle file: '+ repr(filename))
def AppendConfig(self, f):
f.write('\n')
i1=" "
i2=i1+i1
# Make sure we start with a clean slate
f.write(i2+'self.effect.reset()\n')
pos = self.getPos()
hpr = self.getHpr()
scale = self.getScale()
f.write(i2+'self.effect.setPos(%0.3f, %0.3f, %0.3f)\n' %
(pos[0], pos[1], pos[2]))
f.write(i2+'self.effect.setHpr(%0.3f, %0.3f, %0.3f)\n' %
(hpr[0], hpr[1], hpr[2]))
f.write(i2+'self.effect.setScale(%0.3f, %0.3f, %0.3f)\n' %
(scale[0], scale[1], scale[2]))
# Save all the particles to file
num = 0
for p in self.particlesDict.values():
target = 'p%d' % num
num = num + 1
f.write(i2+"if(mode==0):\n")
f.write(i2+i1+target + ' = seParticles.Particles(\'%s\')\n' % p.getName())
f.write(i2+"else:\n")
f.write(i2+i1+target + ' = Particles.Particles(\'%s\')\n' % p.getName())
p.printParams(f, target)
f.write(i2+'self.effect.addParticles(%s)\n' % target)
# Save all the forces to file
num = 0
for fg in self.forceGroupDict.values():
target = 'f%d' % num
num = num + 1
f.write(i2+target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
fg.getName())
fg.printParams(f, target)
f.write(i2+'self.effect.addForceGroup(%s)\n' % target)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,449 @@
from pandac.PandaModules import *
from direct.particles.ParticleManagerGlobal import *
from direct.showbase.PhysicsManagerGlobal import *
#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
from pandac.PandaModules import ParticleSystem
from pandac.PandaModules import BaseParticleFactory
from pandac.PandaModules import PointParticleFactory
from pandac.PandaModules import ZSpinParticleFactory
#import OrientedParticleFactory
from pandac.PandaModules import BaseParticleRenderer
from pandac.PandaModules import PointParticleRenderer
from pandac.PandaModules import LineParticleRenderer
from pandac.PandaModules import GeomParticleRenderer
from pandac.PandaModules import SparkleParticleRenderer
from pandac.PandaModules import SpriteParticleRenderer
from pandac.PandaModules import BaseParticleEmitter
from pandac.PandaModules import BoxEmitter
from pandac.PandaModules import DiscEmitter
from pandac.PandaModules import LineEmitter
from pandac.PandaModules import PointEmitter
from pandac.PandaModules import RectangleEmitter
from pandac.PandaModules import RingEmitter
from pandac.PandaModules import SphereSurfaceEmitter
from pandac.PandaModules import SphereVolumeEmitter
from pandac.PandaModules import TangentRingEmitter
import string
import os
from direct.directnotify import DirectNotifyGlobal
import sys
class Particles(ParticleSystem):
notify = DirectNotifyGlobal.directNotify.newCategory('Particles')
id = 1
def __init__(self, name=None, poolSize=1024):
"""__init__(name, poolSize)"""
if (name == None):
self.name = 'particles-%d' % Particles.id
Particles.id += 1
else:
self.name = name
ParticleSystem.ParticleSystem.__init__(self, poolSize)
# self.setBirthRate(0.02)
# self.setLitterSize(10)
# self.setLitterSpread(0)
# Set up a physical node
self.node = PhysicalNode(self.name)
self.nodePath = NodePath(self.node)
self.setRenderParent(self.node)
self.node.addPhysical(self)
self.factory = None
self.factoryType = "undefined"
# self.setFactory("PointParticleFactory")
self.renderer = None
self.rendererType = "undefined"
# self.setRenderer("PointParticleRenderer")
self.emitter = None
self.emitterType = "undefined"
# self.setEmitter("SphereVolumeEmitter")
# Enable particles by default
self.fEnabled = 0
#self.enable()
def cleanup(self):
self.disable()
self.clearLinearForces()
self.clearAngularForces()
self.setRenderParent(self.node)
self.node.removePhysical(self)
self.nodePath.removeNode()
del self.node
del self.nodePath
del self.factory
del self.renderer
del self.emitter
def enable(self):
"""enable()"""
if (self.fEnabled == 0):
physicsMgr.attachPhysical(self)
particleMgr.attachParticlesystem(self)
self.fEnabled = 1
def disable(self):
"""disable()"""
if (self.fEnabled == 1):
physicsMgr.removePhysical(self)
particleMgr.removeParticlesystem(self)
self.fEnabled = 0
def isEnabled(self):
return self.fEnabled
def getNode(self):
return self.node
def setFactory(self, type):
"""setFactory(type)"""
if (self.factoryType == type):
return None
if (self.factory):
self.factory = None
self.factoryType = type
if (type == "PointParticleFactory"):
self.factory = PointParticleFactory.PointParticleFactory()
elif (type == "ZSpinParticleFactory"):
self.factory = ZSpinParticleFactory.ZSpinParticleFactory()
elif (type == "OrientedParticleFactory"):
self.factory = OrientedParticleFactory.OrientedParticleFactory()
else:
print "unknown factory type: %s" % type
return None
self.factory.setLifespanBase(0.5)
ParticleSystem.ParticleSystem.setFactory(self, self.factory)
def setRenderer(self, type):
"""setRenderer(type)"""
if (self.rendererType == type):
return None
if (self.renderer):
self.renderer = None
self.rendererType = type
if (type == "PointParticleRenderer"):
self.renderer = PointParticleRenderer.PointParticleRenderer()
self.renderer.setPointSize(1.0)
elif (type == "LineParticleRenderer"):
self.renderer = LineParticleRenderer.LineParticleRenderer()
elif (type == "GeomParticleRenderer"):
self.renderer = GeomParticleRenderer.GeomParticleRenderer()
npath = NodePath('default-geom')
# This was moved here because we do not want to download
# the direct tools with toontown.
from direct.directtools import DirectSelection
bbox = DirectSelection.DirectBoundingBox(npath)
self.renderer.setGeomNode(bbox.lines.node())
elif (type == "SparkleParticleRenderer"):
self.renderer = SparkleParticleRenderer.SparkleParticleRenderer()
elif (type == "SpriteParticleRenderer"):
self.renderer = SpriteParticleRenderer.SpriteParticleRenderer()
if (self.renderer.getSourceType() ==
SpriteParticleRenderer.SpriteParticleRenderer.STTexture):
# Use current default texture
# See sourceTextureName SpriteParticleRenderer-extensions.py
self.renderer.setTextureFromFile()
else:
# Use current default model file and node
# See sourceFileName and sourceNodeName in SpriteParticleRenderer-extensions.py
self.renderer.setTextureFromNode()
else:
print "unknown renderer type: %s" % type
return None
ParticleSystem.ParticleSystem.setRenderer(self, self.renderer)
def setEmitter(self, type):
"""setEmitter(type)"""
if (self.emitterType == type):
return None
if (self.emitter):
self.emitter = None
self.emitterType = type
if (type == "BoxEmitter"):
self.emitter = BoxEmitter.BoxEmitter()
elif (type == "DiscEmitter"):
self.emitter = DiscEmitter.DiscEmitter()
elif (type == "LineEmitter"):
self.emitter = LineEmitter.LineEmitter()
elif (type == "PointEmitter"):
self.emitter = PointEmitter.PointEmitter()
elif (type == "RectangleEmitter"):
self.emitter = RectangleEmitter.RectangleEmitter()
elif (type == "RingEmitter"):
self.emitter = RingEmitter.RingEmitter()
elif (type == "SphereSurfaceEmitter"):
self.emitter = SphereSurfaceEmitter.SphereSurfaceEmitter()
elif (type == "SphereVolumeEmitter"):
self.emitter = SphereVolumeEmitter.SphereVolumeEmitter()
self.emitter.setRadius(1.0)
elif (type == "TangentRingEmitter"):
self.emitter = TangentRingEmitter.TangentRingEmitter()
else:
print "unknown emitter type: %s" % type
return None
ParticleSystem.ParticleSystem.setEmitter(self, self.emitter)
def addForce(self, force):
"""addForce(force)"""
if (force.isLinear()):
self.addLinearForce(force)
else:
self.addAngularForce(force)
def removeForce(self, force):
"""removeForce(force)"""
if (force == None):
self.notify.warning('removeForce() - force == None!')
return
if (force.isLinear()):
self.removeLinearForce(force)
else:
self.removeAngularForce(force)
def setRenderNodePath(self, nodePath):
self.setRenderParent(nodePath.node())
## Getters ##
def getName(self):
"""getName()"""
return self.name
def getFactory(self):
"""getFactory()"""
return self.factory
def getEmitter(self):
"""getEmitter()"""
return self.emitter
def getRenderer(self):
"""getRenderer()"""
return self.renderer
def printParams(self, file = sys.stdout, targ = 'self'):
"""printParams(file, targ)"""
i1=" "
i2=i1+i1
file.write(i2+'# Particles parameters\n')
file.write(i2+targ + '.setFactory(\"' + self.factoryType + '\")\n')
file.write(i2+targ + '.setRenderer(\"' + self.rendererType + '\")\n')
file.write(i2+targ + '.setEmitter(\"' + self.emitterType + '\")\n')
# System parameters
file.write(i2+targ + ('.setPoolSize(%d)\n' %
int(self.getPoolSize())))
file.write(i2+targ + ('.setBirthRate(%.4f)\n' %
self.getBirthRate()))
file.write(i2+targ + ('.setLitterSize(%d)\n' %
int(self.getLitterSize())))
file.write(i2+targ + ('.setLitterSpread(%d)\n' %
self.getLitterSpread()))
file.write(i2+targ + ('.setSystemLifespan(%.4f)\n' %
self.getSystemLifespan()))
file.write(i2+targ + ('.setLocalVelocityFlag(%d)\n' %
self.getLocalVelocityFlag()))
file.write(i2+targ + ('.setSystemGrowsOlderFlag(%d)\n' %
self.getSystemGrowsOlderFlag()))
file.write(i2+'# Factory parameters\n')
file.write(i2+targ + ('.factory.setLifespanBase(%.4f)\n' %
self.factory.getLifespanBase()))
file.write(i2+targ + '.factory.setLifespanSpread(%.4f)\n' % \
self.factory.getLifespanSpread())
file.write(i2+targ + '.factory.setMassBase(%.4f)\n' % \
self.factory.getMassBase())
file.write(i2+targ + '.factory.setMassSpread(%.4f)\n' % \
self.factory.getMassSpread())
file.write(i2+targ + '.factory.setTerminalVelocityBase(%.4f)\n' % \
self.factory.getTerminalVelocityBase())
file.write(i2+targ + '.factory.setTerminalVelocitySpread(%.4f)\n' % \
self.factory.getTerminalVelocitySpread())
if (self.factoryType == "PointParticleFactory"):
file.write(i2+'# Point factory parameters\n')
elif (self.factoryType == "ZSpinParticleFactory"):
file.write(i2+'# Z Spin factory parameters\n')
file.write(i2+targ + '.factory.setInitialAngle(%.4f)\n' % \
self.factory.getInitialAngle())
file.write(i2+targ + '.factory.setInitialAngleSpread(%.4f)\n' % \
self.factory.getInitialAngleSpread())
file.write(i2+targ + '.factory.enableAngularVelocity(%d)\n' % \
self.factory.getAngularVelocityEnabled())
if(self.factory.getAngularVelocityEnabled()):
file.write(i2+targ + '.factory.setAngularVelocity(%.4f)\n' % \
self.factory.getAngularVelocity())
file.write(i2+targ + '.factory.setAngularVelocitySpread(%.4f)\n' % \
self.factory.getAngularVelocitySpread())
else:
file.write(i2+targ + '.factory.setFinalAngle(%.4f)\n' % \
self.factory.getFinalAngle())
file.write(i2+targ + '.factory.setFinalAngleSpread(%.4f)\n' % \
self.factory.getFinalAngleSpread())
elif (self.factoryType == "OrientedParticleFactory"):
file.write(i2+'# Oriented factory parameters\n')
file.write(i2+targ + '.factory.setInitialOrientation(%.4f)\n' % \
self.factory.getInitialOrientation())
file.write(i2+targ + '.factory.setFinalOrientation(%.4f)\n' % \
self.factory.getFinalOrientation())
file.write(i2+'# Renderer parameters\n')
alphaMode = self.renderer.getAlphaMode()
aMode = "PRALPHANONE"
if (alphaMode == BaseParticleRenderer.BaseParticleRenderer.PRALPHANONE):
aMode = "PRALPHANONE"
elif (alphaMode ==
BaseParticleRenderer.BaseParticleRenderer.PRALPHAOUT):
aMode = "PRALPHAOUT"
elif (alphaMode ==
BaseParticleRenderer.BaseParticleRenderer.PRALPHAIN):
aMode = "PRALPHAIN"
elif (alphaMode ==
BaseParticleRenderer.BaseParticleRenderer.PRALPHAUSER):
aMode = "PRALPHAUSER"
file.write(i2+targ + '.renderer.setAlphaMode(BaseParticleRenderer.' + aMode + ')\n')
file.write(i2+targ + '.renderer.setUserAlpha(%.2f)\n' % \
self.renderer.getUserAlpha())
if (self.rendererType == "PointParticleRenderer"):
file.write(i2+'# Point parameters\n')
file.write(i2+targ + '.renderer.setPointSize(%.2f)\n' % \
self.renderer.getPointSize())
sColor = self.renderer.getStartColor()
file.write(i2+(targ + '.renderer.setStartColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
sColor = self.renderer.getEndColor()
file.write(i2+(targ + '.renderer.setEndColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
blendType = self.renderer.getBlendType()
bType = "PPONECOLOR"
if (blendType == PointParticleRenderer.PointParticleRenderer.PPONECOLOR):
bType = "PPONECOLOR"
elif (blendType == PointParticleRenderer.PointParticleRenderer.PPBLENDLIFE):
bType = "PPBLENDLIFE"
elif (blendType == PointParticleRenderer.PointParticleRenderer.PPBLENDVEL):
bType = "PPBLENDVEL"
file.write(i2+targ + '.renderer.setBlendType(PointParticleRenderer.' + bType + ')\n')
blendMethod = self.renderer.getBlendMethod()
bMethod = "PPNOBLEND"
if (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPNOBLEND):
bMethod = "PPNOBLEND"
elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDLINEAR):
bMethod = "PPBLENDLINEAR"
elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDCUBIC):
bMethod = "PPBLENDCUBIC"
file.write(i2+targ + '.renderer.setBlendMethod(BaseParticleRenderer.' + bMethod + ')\n')
elif (self.rendererType == "LineParticleRenderer"):
file.write(i2+'# Line parameters\n')
sColor = self.renderer.getHeadColor()
file.write(i2+(targ + '.renderer.setHeadColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
sColor = self.renderer.getTailColor()
file.write(i2+(targ + '.renderer.setTailColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
elif (self.rendererType == "GeomParticleRenderer"):
file.write(i2+'# Geom parameters\n')
node = self.renderer.getGeomNode()
file.write(i2+targ + '.renderer.setGeomNode(' + node.getName() + ')\n')
elif (self.rendererType == "SparkleParticleRenderer"):
file.write(i2+'# Sparkle parameters\n')
sColor = self.renderer.getCenterColor()
file.write(i2+(targ + '.renderer.setCenterColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
sColor = self.renderer.getEdgeColor()
file.write(i2+(targ + '.renderer.setEdgeColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
file.write(i2+targ + '.renderer.setBirthRadius(%.4f)\n' % self.renderer.getBirthRadius())
file.write(i2+targ + '.renderer.setDeathRadius(%.4f)\n' % self.renderer.getDeathRadius())
lifeScale = self.renderer.getLifeScale()
lScale = "SPNOSCALE"
if (lifeScale == SparkleParticleRenderer.SparkleParticleRenderer.SPSCALE):
lScale = "SPSCALE"
file.write(i2+targ + '.renderer.setLifeScale(SparkleParticleRenderer.' + lScale + ')\n')
elif (self.rendererType == "SpriteParticleRenderer"):
file.write(i2+'# Sprite parameters\n')
if (self.renderer.getSourceType() ==
SpriteParticleRenderer.SpriteParticleRenderer.STTexture):
tex = self.renderer.getTexture()
file.write(i2+targ + '.renderer.setTexture(loader.loadTexture(\'' + tex.getFilename().getFullpath() + '\'))\n')
else:
modelName = self.renderer.getSourceFileName()
nodeName = self.renderer.getSourceNodeName()
file.write(i2+targ + '.renderer.setTextureFromNode("%s", "%s")\n' % (modelName, nodeName))
sColor = self.renderer.getColor()
file.write(i2+(targ + '.renderer.setColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3])))
file.write(i2+targ + '.renderer.setXScaleFlag(%d)\n' % self.renderer.getXScaleFlag())
file.write(i2+targ + '.renderer.setYScaleFlag(%d)\n' % self.renderer.getYScaleFlag())
file.write(i2+targ + '.renderer.setAnimAngleFlag(%d)\n' % self.renderer.getAnimAngleFlag())
file.write(i2+targ + '.renderer.setInitialXScale(%.4f)\n' % self.renderer.getInitialXScale())
file.write(i2+targ + '.renderer.setFinalXScale(%.4f)\n' % self.renderer.getFinalXScale())
file.write(i2+targ + '.renderer.setInitialYScale(%.4f)\n' % self.renderer.getInitialYScale())
file.write(i2+targ + '.renderer.setFinalYScale(%.4f)\n' % self.renderer.getFinalYScale())
file.write(i2+targ + '.renderer.setNonanimatedTheta(%.4f)\n' % self.renderer.getNonanimatedTheta())
blendMethod = self.renderer.getAlphaBlendMethod()
bMethod = "PPNOBLEND"
if (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPNOBLEND):
bMethod = "PPNOBLEND"
elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDLINEAR):
bMethod = "PPBLENDLINEAR"
elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDCUBIC):
bMethod = "PPBLENDCUBIC"
file.write(i2+targ + '.renderer.setAlphaBlendMethod(BaseParticleRenderer.' + bMethod + ')\n')
file.write(i2+targ + '.renderer.setAlphaDisable(%d)\n' % self.renderer.getAlphaDisable())
file.write(i2+'# Emitter parameters\n')
emissionType = self.emitter.getEmissionType()
eType = "ETEXPLICIT"
if (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETEXPLICIT):
eType = "ETEXPLICIT"
elif (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETRADIATE):
eType = "ETRADIATE"
elif (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETCUSTOM):
eType = "ETCUSTOM"
file.write(i2+targ + '.emitter.setEmissionType(BaseParticleEmitter.' + eType + ')\n')
file.write(i2+targ + '.emitter.setAmplitude(%.4f)\n' % self.emitter.getAmplitude())
file.write(i2+targ + '.emitter.setAmplitudeSpread(%.4f)\n' % self.emitter.getAmplitudeSpread())
oForce = self.emitter.getOffsetForce()
file.write(i2+(targ + '.emitter.setOffsetForce(Vec3(%.4f, %.4f, %.4f))\n' % (oForce[0], oForce[1], oForce[2])))
oForce = self.emitter.getExplicitLaunchVector()
file.write(i2+(targ + '.emitter.setExplicitLaunchVector(Vec3(%.4f, %.4f, %.4f))\n' % (oForce[0], oForce[1], oForce[2])))
orig = self.emitter.getRadiateOrigin()
file.write(i2+(targ + '.emitter.setRadiateOrigin(Point3(%.4f, %.4f, %.4f))\n' % (orig[0], orig[1], orig[2])))
if (self.emitterType == "BoxEmitter"):
file.write(i2+'# Box parameters\n')
bound = self.emitter.getMinBound()
file.write(i2+(targ + '.emitter.setMinBound(Point3(%.4f, %.4f, %.4f))\n' % (bound[0], bound[1], bound[2])))
bound = self.emitter.getMaxBound()
file.write(i2+(targ + '.emitter.setMaxBound(Point3(%.4f, %.4f, %.4f))\n' % (bound[0], bound[1], bound[2])))
elif (self.emitterType == "DiscEmitter"):
file.write(i2+'# Disc parameters\n')
file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius())
if (eType == "ETCUSTOM"):
file.write(i2+targ + '.emitter.setOuterAngle(%.4f)\n' % self.emitter.getOuterAngle())
file.write(i2+targ + '.emitter.setInnerAngle(%.4f)\n' % self.emitter.getInnerAngle())
file.write(i2+targ + '.emitter.setOuterMagnitude(%.4f)\n' % self.emitter.getOuterMagnitude())
file.write(i2+targ + '.emitter.setInnerMagnitude(%.4f)\n' % self.emitter.getInnerMagnitude())
file.write(i2+targ + '.emitter.setCubicLerping(%d)\n' % self.emitter.getCubicLerping())
elif (self.emitterType == "LineEmitter"):
file.write(i2+'# Line parameters\n')
point = self.emitter.getEndpoint1()
file.write(i2+(targ + '.emitter.setEndpoint1(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2])))
point = self.emitter.getEndpoint2()
file.write(i2+(targ + '.emitter.setEndpoint2(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2])))
elif (self.emitterType == "PointEmitter"):
file.write(i2+'# Point parameters\n')
point = self.emitter.getLocation()
file.write(i2+(targ + '.emitter.setLocation(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2])))
elif (self.emitterType == "RectangleEmitter"):
file.write(i2+'# Rectangle parameters\n')
point = self.emitter.getMinBound()
file.write(i2+(targ + '.emitter.setMinBound(Point2(%.4f, %.4f))\n' % (point[0], point[1])))
point = self.emitter.getMaxBound()
file.write(i2+(targ + '.emitter.setMaxBound(Point2(%.4f, %.4f))\n' % (point[0], point[1])))
elif (self.emitterType == "RingEmitter"):
file.write(i2+'# Ring parameters\n')
file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius())
if (eType == "ETCUSTOM"):
file.write(i2+targ + '.emitter.setAngle(%.4f)\n' % self.emitter.getAngle())
elif (self.emitterType == "SphereSurfaceEmitter"):
file.write(i2+'# Sphere Surface parameters\n')
file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius())
elif (self.emitterType == "SphereVolumeEmitter"):
file.write(i2+'# Sphere Volume parameters\n')
file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius())
elif (self.emitterType == "TangentRingEmitter"):
file.write(i2+'# Tangent Ring parameters\n')
file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius())

View file

@ -0,0 +1,800 @@
""" DIRECT Nine DoF Manipulation Panel """
from direct.showbase.DirectObject import DirectObject
from direct.directtools.DirectGlobals import *
from direct.tkwidgets.AppShell import AppShell
from direct.tkwidgets.Dial import AngleDial
from direct.tkwidgets.Floater import Floater
from Tkinter import Button, Menubutton, Menu, StringVar
from pandac.PandaModules import *
import Tkinter, Pmw
"""
TODO:
Task to monitor pose
"""
class Placer(AppShell):
# Override class variables here
appname = 'Placer Panel'
frameWidth = 625
frameHeight = 215
usecommandarea = 0
usestatusarea = 0
def __init__(self, parent = None, **kw):
INITOPT = Pmw.INITOPT
optiondefs = (
('title', self.appname, None),
('nodePath', SEditor.camera, None),
)
self.defineoptions(kw, optiondefs)
# Call superclass initialization function
AppShell.__init__(self)
self.initialiseoptions(Placer)
# Accept the message from sceneEditor to update the information about the target nodePath
self.accept('placerUpdate', self.updatePlacer)
def appInit(self):
# Initialize state
self.tempCS = SEditor.group.attachNewNode('placerTempCS')
self.orbitFromCS = SEditor.group.attachNewNode(
'placerOrbitFromCS')
self.orbitToCS = SEditor.group.attachNewNode('placerOrbitToCS')
self.refCS = self.tempCS
# Dictionary keeping track of all node paths manipulated so far
self.nodePathDict = {}
self.nodePathDict['camera'] = SEditor.camera
self.nodePathDict['widget'] = SEditor.widget
self.nodePathNames = ['camera', 'widget', 'selected']
self.refNodePathDict = {}
self.refNodePathDict['parent'] = self['nodePath'].getParent()
self.refNodePathDict['render'] = render
self.refNodePathDict['camera'] = SEditor.camera
self.refNodePathDict['widget'] = SEditor.widget
self.refNodePathNames = ['parent', 'self', 'render',
'camera', 'widget', 'selected']
# Initial state
self.initPos = Vec3(0)
self.initHpr = Vec3(0)
self.initScale = Vec3(1)
self.deltaHpr = Vec3(0)
# Offset for orbital mode
self.posOffset = Vec3(0)
# Set up event hooks
self.undoEvents = [('DIRECT_undo', self.undoHook),
('DIRECT_pushUndo', self.pushUndoHook),
('DIRECT_undoListEmpty', self.undoListEmptyHook),
('DIRECT_redo', self.redoHook),
('DIRECT_pushRedo', self.pushRedoHook),
('DIRECT_redoListEmpty', self.redoListEmptyHook)]
for event, method in self.undoEvents:
self.accept(event, method)
# Init movement mode
self.movementMode = 'Relative To:'
def createInterface(self):
# The interior of the toplevel panel
interior = self.interior()
interior['relief'] = Tkinter.FLAT
# Add placer commands to menubar
self.menuBar.addmenu('Placer', 'Placer Panel Operations')
self.menuBar.addmenuitem('Placer', 'command',
'Zero Node Path',
label = 'Zero All',
command = self.zeroAll)
self.menuBar.addmenuitem('Placer', 'command',
'Reset Node Path',
label = 'Reset All',
command = self.resetAll)
self.menuBar.addmenuitem('Placer', 'command',
'Print Node Path Info',
label = 'Print Info',
command = self.printNodePathInfo)
self.menuBar.addmenuitem(
'Placer', 'command',
'Toggle widget visability',
label = 'Toggle Widget Vis',
command = SEditor.toggleWidgetVis)
self.menuBar.addmenuitem(
'Placer', 'command',
'Toggle widget manipulation mode',
label = 'Toggle Widget Mode',
command = SEditor.manipulationControl.toggleObjectHandlesMode)
# Get a handle to the menu frame
menuFrame = self.menuFrame
self.nodePathMenu = Pmw.ComboBox(
menuFrame, labelpos = Tkinter.W, label_text = 'Node Path:',
entry_width = 20,
selectioncommand = self.selectNodePathNamed,
scrolledlist_items = self.nodePathNames)
self.nodePathMenu.selectitem('selected')
self.nodePathMenuEntry = (
self.nodePathMenu.component('entryfield_entry'))
self.nodePathMenuBG = (
self.nodePathMenuEntry.configure('background')[3])
self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
self.bind(self.nodePathMenu, 'Select node path to manipulate')
modeMenu = Pmw.OptionMenu(menuFrame,
items = ('Relative To:',
'Orbit:'),
initialitem = 'Relative To:',
command = self.setMovementMode,
menubutton_width = 8)
modeMenu.pack(side = 'left', expand = 0)
self.bind(modeMenu, 'Select manipulation mode')
self.refNodePathMenu = Pmw.ComboBox(
menuFrame, entry_width = 16,
selectioncommand = self.selectRefNodePathNamed,
scrolledlist_items = self.refNodePathNames)
self.refNodePathMenu.selectitem('parent')
self.refNodePathMenuEntry = (
self.refNodePathMenu.component('entryfield_entry'))
self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
self.bind(self.refNodePathMenu, 'Select relative node path')
self.undoButton = Button(menuFrame, text = 'Undo',
command = SEditor.undo)
if SEditor.undoList:
self.undoButton['state'] = 'normal'
else:
self.undoButton['state'] = 'disabled'
self.undoButton.pack(side = 'left', expand = 0)
self.bind(self.undoButton, 'Undo last operation')
self.redoButton = Button(menuFrame, text = 'Redo',
command = SEditor.redo)
if SEditor.redoList:
self.redoButton['state'] = 'normal'
else:
self.redoButton['state'] = 'disabled'
self.redoButton.pack(side = 'left', expand = 0)
self.bind(self.redoButton, 'Redo last operation')
# Create and pack the Pos Controls
posGroup = Pmw.Group(interior,
tag_pyclass = Menubutton,
tag_text = 'Position',
tag_font=('MSSansSerif', 14),
tag_activebackground = '#909090',
ring_relief = Tkinter.RIDGE)
posMenubutton = posGroup.component('tag')
self.bind(posMenubutton, 'Position menu operations')
posMenu = Menu(posMenubutton, tearoff = 0)
posMenu.add_command(label = 'Set to zero', command = self.zeroPos)
posMenu.add_command(label = 'Reset initial',
command = self.resetPos)
posMenubutton['menu'] = posMenu
posGroup.pack(side='left', fill = 'both', expand = 1)
posInterior = posGroup.interior()
# Create the dials
self.posX = self.createcomponent('posX', (), None,
Floater, (posInterior,),
text = 'X', relief = Tkinter.FLAT,
value = 0.0,
label_foreground = 'Red')
self.posX['commandData'] = ['x']
self.posX['preCallback'] = self.xformStart
self.posX['postCallback'] = self.xformStop
self.posX['callbackData'] = ['x']
self.posX.pack(expand=1,fill='both')
self.posY = self.createcomponent('posY', (), None,
Floater, (posInterior,),
text = 'Y', relief = Tkinter.FLAT,
value = 0.0,
label_foreground = '#00A000')
self.posY['commandData'] = ['y']
self.posY['preCallback'] = self.xformStart
self.posY['postCallback'] = self.xformStop
self.posY['callbackData'] = ['y']
self.posY.pack(expand=1,fill='both')
self.posZ = self.createcomponent('posZ', (), None,
Floater, (posInterior,),
text = 'Z', relief = Tkinter.FLAT,
value = 0.0,
label_foreground = 'Blue')
self.posZ['commandData'] = ['z']
self.posZ['preCallback'] = self.xformStart
self.posZ['postCallback'] = self.xformStop
self.posZ['callbackData'] = ['z']
self.posZ.pack(expand=1,fill='both')
# Create and pack the Hpr Controls
hprGroup = Pmw.Group(interior,
tag_pyclass = Menubutton,
tag_text = 'Orientation',
tag_font=('MSSansSerif', 14),
tag_activebackground = '#909090',
ring_relief = Tkinter.RIDGE)
hprMenubutton = hprGroup.component('tag')
self.bind(hprMenubutton, 'Orientation menu operations')
hprMenu = Menu(hprMenubutton, tearoff = 0)
hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr)
hprMenu.add_command(label = 'Reset initial', command = self.resetHpr)
hprMenubutton['menu'] = hprMenu
hprGroup.pack(side='left',fill = 'both', expand = 1)
hprInterior = hprGroup.interior()
# Create the dials
self.hprH = self.createcomponent('hprH', (), None,
AngleDial, (hprInterior,),
style = 'mini',
text = 'H', value = 0.0,
relief = Tkinter.FLAT,
label_foreground = 'blue')
self.hprH['commandData'] = ['h']
self.hprH['preCallback'] = self.xformStart
self.hprH['postCallback'] = self.xformStop
self.hprH['callbackData'] = ['h']
self.hprH.pack(expand=1,fill='both')
self.hprP = self.createcomponent('hprP', (), None,
AngleDial, (hprInterior,),
style = 'mini',
text = 'P', value = 0.0,
relief = Tkinter.FLAT,
label_foreground = 'red')
self.hprP['commandData'] = ['p']
self.hprP['preCallback'] = self.xformStart
self.hprP['postCallback'] = self.xformStop
self.hprP['callbackData'] = ['p']
self.hprP.pack(expand=1,fill='both')
self.hprR = self.createcomponent('hprR', (), None,
AngleDial, (hprInterior,),
style = 'mini',
text = 'R', value = 0.0,
relief = Tkinter.FLAT,
label_foreground = '#00A000')
self.hprR['commandData'] = ['r']
self.hprR['preCallback'] = self.xformStart
self.hprR['postCallback'] = self.xformStop
self.hprR['callbackData'] = ['r']
self.hprR.pack(expand=1,fill='both')
# Create and pack the Scale Controls
# The available scaling modes
self.scalingMode = StringVar()
self.scalingMode.set('Scale Uniform')
# The scaling widgets
scaleGroup = Pmw.Group(interior,
tag_text = 'Scale Uniform',
tag_pyclass = Menubutton,
tag_font=('MSSansSerif', 14),
tag_activebackground = '#909090',
ring_relief = Tkinter.RIDGE)
self.scaleMenubutton = scaleGroup.component('tag')
self.bind(self.scaleMenubutton, 'Scale menu operations')
self.scaleMenubutton['textvariable'] = self.scalingMode
# Scaling menu
scaleMenu = Menu(self.scaleMenubutton, tearoff = 0)
scaleMenu.add_command(label = 'Set to unity',
command = self.unitScale)
scaleMenu.add_command(label = 'Reset initial',
command = self.resetScale)
scaleMenu.add_radiobutton(label = 'Scale Free',
variable = self.scalingMode)
scaleMenu.add_radiobutton(label = 'Scale Uniform',
variable = self.scalingMode)
scaleMenu.add_radiobutton(label = 'Scale Proportional',
variable = self.scalingMode)
self.scaleMenubutton['menu'] = scaleMenu
# Pack group widgets
scaleGroup.pack(side='left',fill = 'both', expand = 1)
scaleInterior = scaleGroup.interior()
# Create the dials
self.scaleX = self.createcomponent('scaleX', (), None,
Floater, (scaleInterior,),
text = 'X Scale',
relief = Tkinter.FLAT,
min = 0.0001, value = 1.0,
resetValue = 1.0,
label_foreground = 'Red')
self.scaleX['commandData'] = ['sx']
self.scaleX['callbackData'] = ['sx']
self.scaleX['preCallback'] = self.xformStart
self.scaleX['postCallback'] = self.xformStop
self.scaleX.pack(expand=1,fill='both')
self.scaleY = self.createcomponent('scaleY', (), None,
Floater, (scaleInterior,),
text = 'Y Scale',
relief = Tkinter.FLAT,
min = 0.0001, value = 1.0,
resetValue = 1.0,
label_foreground = '#00A000')
self.scaleY['commandData'] = ['sy']
self.scaleY['callbackData'] = ['sy']
self.scaleY['preCallback'] = self.xformStart
self.scaleY['postCallback'] = self.xformStop
self.scaleY.pack(expand=1,fill='both')
self.scaleZ = self.createcomponent('scaleZ', (), None,
Floater, (scaleInterior,),
text = 'Z Scale',
relief = Tkinter.FLAT,
min = 0.0001, value = 1.0,
resetValue = 1.0,
label_foreground = 'Blue')
self.scaleZ['commandData'] = ['sz']
self.scaleZ['callbackData'] = ['sz']
self.scaleZ['preCallback'] = self.xformStart
self.scaleZ['postCallback'] = self.xformStop
self.scaleZ.pack(expand=1,fill='both')
# Make sure appropriate labels are showing
self.setMovementMode('Relative To:')
# Set up placer for inital node path
self.selectNodePathNamed('init')
self.selectRefNodePathNamed('parent')
# Update place to reflect initial state
self.updatePlacer()
# Now that you're done setting up, attach commands
self.posX['command'] = self.xform
self.posY['command'] = self.xform
self.posZ['command'] = self.xform
self.hprH['command'] = self.xform
self.hprP['command'] = self.xform
self.hprR['command'] = self.xform
self.scaleX['command'] = self.xform
self.scaleY['command'] = self.xform
self.scaleZ['command'] = self.xform
### WIDGET OPERATIONS ###
def setMovementMode(self, movementMode):
# Set prefix
namePrefix = ''
self.movementMode = movementMode
if (movementMode == 'Relative To:'):
namePrefix = 'Relative '
elif (movementMode == 'Orbit:'):
namePrefix = 'Orbit '
# Update pos widgets
self.posX['text'] = namePrefix + 'X'
self.posY['text'] = namePrefix + 'Y'
self.posZ['text'] = namePrefix + 'Z'
# Update hpr widgets
if (movementMode == 'Orbit:'):
namePrefix = 'Orbit delta '
self.hprH['text'] = namePrefix + 'H'
self.hprP['text'] = namePrefix + 'P'
self.hprR['text'] = namePrefix + 'R'
# Update temp cs and initialize widgets
self.updatePlacer()
def setScalingMode(self):
if self['nodePath']:
scale = self['nodePath'].getScale()
if ((scale[0] != scale[1]) or
(scale[0] != scale[2]) or
(scale[1] != scale[2])):
self.scalingMode.set('Scale Free')
def selectNodePathNamed(self, name):
nodePath = None
if name == 'init':
nodePath = self['nodePath']
# Add Combo box entry for the initial node path
self.addNodePath(nodePath)
elif name == 'selected':
nodePath = SEditor.selected.last
# Add Combo box entry for this selected object
self.addNodePath(nodePath)
else:
nodePath = self.nodePathDict.get(name, None)
if (nodePath == None):
# See if this evaluates into a node path
try:
nodePath = eval(name)
if isinstance(nodePath, NodePath):
self.addNodePath(nodePath)
else:
# Good eval but not a node path, give up
nodePath = None
except:
# Bogus eval
nodePath = None
# Clear bogus entry from listbox
listbox = self.nodePathMenu.component('scrolledlist')
listbox.setlist(self.nodePathNames)
else:
if name == 'widget':
# Record relationship between selected nodes and widget
SEditor.selected.getWrtAll()
# Update active node path
self.setActiveNodePath(nodePath)
def setActiveNodePath(self, nodePath):
self['nodePath'] = nodePath
if self['nodePath']:
self.nodePathMenuEntry.configure(
background = self.nodePathMenuBG)
# Check to see if node path and ref node path are the same
if ((self.refCS != None) and
(self.refCS.id() == self['nodePath'].id())):
# Yes they are, use temp CS as ref
# This calls updatePlacer
self.setReferenceNodePath(self.tempCS)
# update listbox accordingly
self.refNodePathMenu.selectitem('parent')
else:
# Record initial value and initialize the widgets
self.updatePlacer()
# Record initial position
self.updateResetValues(self['nodePath'])
# Set scaling mode based on node path's current scale
self.setScalingMode()
else:
# Flash entry
self.nodePathMenuEntry.configure(background = 'Pink')
def selectRefNodePathNamed(self, name):
nodePath = None
if name == 'self':
nodePath = self.tempCS
elif name == 'selected':
nodePath = SEditor.selected.last
# Add Combo box entry for this selected object
self.addRefNodePath(nodePath)
elif name == 'parent':
nodePath = self['nodePath'].getParent()
else:
nodePath = self.refNodePathDict.get(name, None)
if (nodePath == None):
# See if this evaluates into a node path
try:
nodePath = eval(name)
if isinstance(nodePath, NodePath):
self.addRefNodePath(nodePath)
else:
# Good eval but not a node path, give up
nodePath = None
except:
# Bogus eval
nodePath = None
# Clear bogus entry from listbox
listbox = self.refNodePathMenu.component('scrolledlist')
listbox.setlist(self.refNodePathNames)
# Check to see if node path and ref node path are the same
if (nodePath != None) and (nodePath.id() == self['nodePath'].id()):
# Yes they are, use temp CS and update listbox accordingly
nodePath = self.tempCS
self.refNodePathMenu.selectitem('parent')
# Update ref node path
self.setReferenceNodePath(nodePath)
def setReferenceNodePath(self, nodePath):
self.refCS = nodePath
if self.refCS:
self.refNodePathMenuEntry.configure(
background = self.nodePathMenuBG)
# Update placer to reflect new state
self.updatePlacer()
else:
# Flash entry
self.refNodePathMenuEntry.configure(background = 'Pink')
def addNodePath(self, nodePath):
self.addNodePathToDict(nodePath, self.nodePathNames,
self.nodePathMenu, self.nodePathDict)
def addRefNodePath(self, nodePath):
self.addNodePathToDict(nodePath, self.refNodePathNames,
self.refNodePathMenu, self.refNodePathDict)
def addNodePathToDict(self, nodePath, names, menu, dict):
if not nodePath:
return
# Get node path's name
name = nodePath.getName()
if name in ['parent', 'render', 'camera']:
dictName = name
else:
# Generate a unique name for the dict
dictName = name + '-' + `nodePath.id()`
if not dict.has_key(dictName):
# Update combo box to include new item
names.append(dictName)
listbox = menu.component('scrolledlist')
listbox.setlist(names)
# Add new item to dictionary
dict[dictName] = nodePath
menu.selectitem(dictName)
def updatePlacer(self):
pos = Vec3(0)
hpr = Vec3(0)
scale = Vec3(1)
np = self['nodePath']
if (np != None) and isinstance(np, NodePath):
# Update temp CS
self.updateAuxiliaryCoordinateSystems()
# Update widgets
if self.movementMode == 'Orbit:':
pos.assign(self.posOffset)
hpr.assign(ZERO_VEC)
scale.assign(np.getScale())
elif self.refCS:
pos.assign(np.getPos(self.refCS))
hpr.assign(np.getHpr(self.refCS))
scale.assign(np.getScale())
self.updatePosWidgets(pos)
self.updateHprWidgets(hpr)
self.updateScaleWidgets(scale)
def updateAuxiliaryCoordinateSystems(self):
# Temp CS
self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0)
# Orbit CS
# At reference
self.orbitFromCS.setPos(self.refCS, 0,0,0)
# But aligned with target
self.orbitFromCS.setHpr(self['nodePath'], 0,0,0)
# Also update to CS
self.orbitToCS.setPosHpr(self.orbitFromCS, 0,0,0,0,0,0)
# Get offset from origin
self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS))
### NODE PATH TRANSFORMATION OPERATIONS ###
def xform(self, value, axis):
if axis in ['sx', 'sy', 'sz']:
self.xformScale(value,axis)
elif self.movementMode == 'Relative To:':
self.xformRelative(value, axis)
elif self.movementMode == 'Orbit:':
self.xformOrbit(value, axis)
if self.nodePathMenu.get() == 'widget':
if SEditor.manipulationControl.fSetCoa:
# Update coa based on current widget position
SEditor.selected.last.mCoa2Dnp.assign(
SEditor.widget.getMat(SEditor.selected.last))
else:
# Move the objects with the widget
SEditor.selected.moveWrtWidgetAll()
def xformStart(self, data):
# Record undo point
self.pushUndo()
# If moving widget kill follow task and update wrts
if self.nodePathMenu.get() == 'widget':
taskMgr.remove('followSelectedNodePath')
# Record relationship between selected nodes and widget
SEditor.selected.getWrtAll()
# Record initial state
self.deltaHpr = self['nodePath'].getHpr(self.refCS)
# Update placer to reflect new state
self.updatePlacer()
def xformStop(self, data):
# Throw event to signal manipulation done
# Send nodepath as a list
messenger.send('DIRECT_manipulateObjectCleanup', [[self['nodePath']]])
# Update placer to reflect new state
self.updatePlacer()
# If moving widget restart follow task
if self.nodePathMenu.get() == 'widget':
# Restart followSelectedNodePath task
SEditor.manipulationControl.spawnFollowSelectedNodePathTask()
def xformRelative(self, value, axis):
nodePath = self['nodePath']
if (nodePath != None) and (self.refCS != None):
if axis == 'x':
nodePath.setX(self.refCS, value)
elif axis == 'y':
nodePath.setY(self.refCS, value)
elif axis == 'z':
nodePath.setZ(self.refCS, value)
else:
if axis == 'h':
self.deltaHpr.setX(value)
elif axis == 'p':
self.deltaHpr.setY(value)
elif axis == 'r':
self.deltaHpr.setZ(value)
# Put node path at new hpr
nodePath.setHpr(self.refCS, self.deltaHpr)
def xformOrbit(self, value, axis):
nodePath = self['nodePath']
if ((nodePath != None) and (self.refCS != None) and
(self.orbitFromCS != None) and (self.orbitToCS != None)):
if axis == 'x':
self.posOffset.setX(value)
elif axis == 'y':
self.posOffset.setY(value)
elif axis == 'z':
self.posOffset.setZ(value)
elif axis == 'h':
self.orbitToCS.setH(self.orbitFromCS, value)
elif axis == 'p':
self.orbitToCS.setP(self.orbitFromCS, value)
elif axis == 'r':
self.orbitToCS.setR(self.orbitFromCS, value)
nodePath.setPosHpr(self.orbitToCS, self.posOffset, ZERO_VEC)
def xformScale(self, value, axis):
if self['nodePath']:
mode = self.scalingMode.get()
scale = self['nodePath'].getScale()
if mode == 'Scale Free':
if axis == 'sx':
scale.setX(value)
elif axis == 'sy':
scale.setY(value)
elif axis == 'sz':
scale.setZ(value)
elif mode == 'Scale Uniform':
scale.set(value,value,value)
elif mode == 'Scale Proportional':
if axis == 'sx':
sf = value/scale[0]
elif axis == 'sy':
sf = value/scale[1]
elif axis == 'sz':
sf = value/scale[2]
scale = scale * sf
self['nodePath'].setScale(scale)
def updatePosWidgets(self, pos):
self.posX.set(pos[0])
self.posY.set(pos[1])
self.posZ.set(pos[2])
def updateHprWidgets(self, hpr):
self.hprH.set(hpr[0])
self.hprP.set(hpr[1])
self.hprR.set(hpr[2])
def updateScaleWidgets(self, scale):
self.scaleX.set(scale[0])
self.scaleY.set(scale[1])
self.scaleZ.set(scale[2])
def zeroAll(self):
self.xformStart(None)
self.updatePosWidgets(ZERO_VEC)
self.updateHprWidgets(ZERO_VEC)
self.updateScaleWidgets(UNIT_VEC)
self.xformStop(None)
def zeroPos(self):
self.xformStart(None)
self.updatePosWidgets(ZERO_VEC)
self.xformStop(None)
def zeroHpr(self):
self.xformStart(None)
self.updateHprWidgets(ZERO_VEC)
self.xformStop(None)
def unitScale(self):
self.xformStart(None)
self.updateScaleWidgets(UNIT_VEC)
self.xformStop(None)
def updateResetValues(self, nodePath):
self.initPos.assign(nodePath.getPos())
self.posX['resetValue'] = self.initPos[0]
self.posY['resetValue'] = self.initPos[1]
self.posZ['resetValue'] = self.initPos[2]
self.initHpr.assign(nodePath.getHpr())
self.hprH['resetValue'] = self.initHpr[0]
self.hprP['resetValue'] = self.initHpr[1]
self.hprR['resetValue'] = self.initHpr[2]
self.initScale.assign(nodePath.getScale())
self.scaleX['resetValue'] = self.initScale[0]
self.scaleY['resetValue'] = self.initScale[1]
self.scaleZ['resetValue'] = self.initScale[2]
def resetAll(self):
if self['nodePath']:
self.xformStart(None)
self['nodePath'].setPosHprScale(
self.initPos, self.initHpr, self.initScale)
self.xformStop(None)
def resetPos(self):
if self['nodePath']:
self.xformStart(None)
self['nodePath'].setPos(self.initPos)
self.xformStop(None)
def resetHpr(self):
if self['nodePath']:
self.xformStart(None)
self['nodePath'].setHpr(self.initHpr)
self.xformStop(None)
def resetScale(self):
if self['nodePath']:
self.xformStart(None)
self['nodePath'].setScale(self.initScale)
self.xformStop(None)
def pushUndo(self, fResetRedo = 1):
SEditor.pushUndo([self['nodePath']])
def undoHook(self, nodePathList = []):
# Reflect new changes
self.updatePlacer()
def pushUndoHook(self):
# Make sure button is reactivated
self.undoButton.configure(state = 'normal')
def undoListEmptyHook(self):
# Make sure button is deactivated
self.undoButton.configure(state = 'disabled')
def pushRedo(self):
SEditor.pushRedo([self['nodePath']])
def redoHook(self, nodePathList = []):
# Reflect new changes
self.updatePlacer()
def pushRedoHook(self):
# Make sure button is reactivated
self.redoButton.configure(state = 'normal')
def redoListEmptyHook(self):
# Make sure button is deactivated
self.redoButton.configure(state = 'disabled')
def printNodePathInfo(self):
np = self['nodePath']
if np:
name = np.getName()
pos = np.getPos()
hpr = np.getHpr()
scale = np.getScale()
posString = '%.2f, %.2f, %.2f' % (pos[0], pos[1], pos[2])
hprString = '%.2f, %.2f, %.2f' % (hpr[0], hpr[1], hpr[2])
scaleString = '%.2f, %.2f, %.2f' % (scale[0], scale[1], scale[2])
print 'NodePath: %s' % name
print 'Pos: %s' % posString
print 'Hpr: %s' % hprString
print 'Scale: %s' % scaleString
print ('%s.setPosHprScale(%s, %s, %s)' %
(name, posString, hprString, scaleString))
def onDestroy(self, event):
# Remove hooks
for event, method in self.undoEvents:
self.ignore(event)
self.tempCS.removeNode()
self.orbitFromCS.removeNode()
self.orbitToCS.removeNode()
# send out a message to let sceneEditor know that placer panel has been closed.
# Also, stop accepting the updata message from sceneEditor
messenger.send('Placer_close')
self.ignore('placerUpdate')
def place(nodePath):
return Placer(nodePath = nodePath)
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
root = Pmw.initialise()
widget = Placer()

View file

@ -0,0 +1,209 @@
#################################################################
# seSceneGraphExplorer.py
# Originally from SceneGraphExplorer.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# we need a customized SceneGraphExplorer.
#
# Do forget to check the seTree.
#
#################################################################
from direct.showbase.DirectObject import DirectObject
from Tkinter import IntVar, Frame, Label
from seTree import TreeNode, TreeItem
import Pmw, Tkinter
# changing these strings requires changing sceneEditor.py SGE_ strs too!
# This list of items will be showed on the pop out window when user right click on
# any node on the graph. And, this is also the main reason we decide to copy from
# the original one but not inherited from it.
# Because except drawing part, we have changed a lot of things...
DEFAULT_MENU_ITEMS = [
'Update Explorer',
'Separator',
'Properties',
'Separator',
'Duplicate',
'Remove',
'Add Dummy',
'Add Collision Object',
'Metadata',
'Separator',
'Set as Reparent Target',
'Reparent to Target',
'Separator',
'Animation Panel',
'Blend Animation Panel',
'MoPath Panel',
'Align Tool',
'Separator']
class seSceneGraphExplorer(Pmw.MegaWidget, DirectObject):
"Graphical display of a scene graph"
def __init__(self, parent = None, nodePath = render, **kw):
# Define the megawidget options.
optiondefs = (
('menuItems', [], Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
# Initialise superclass
Pmw.MegaWidget.__init__(self, parent)
# Initialize some class variables
self.nodePath = nodePath
# Create the components.
# Setup up container
interior = self.interior()
interior.configure(relief = Tkinter.GROOVE, borderwidth = 2)
# Create a label and an entry
self._scrolledCanvas = self.createcomponent(
'scrolledCanvas',
(), None,
Pmw.ScrolledCanvas, (interior,),
hull_width = 200, hull_height = 300,
usehullsize = 1)
self._canvas = self._scrolledCanvas.component('canvas')
self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i')
self._scrolledCanvas.resizescrollregion()
self._scrolledCanvas.pack(padx = 3, pady = 3, expand=1, fill = Tkinter.BOTH)
self._canvas.bind('<ButtonPress-2>', self.mouse2Down)
self._canvas.bind('<B2-Motion>', self.mouse2Motion)
self._canvas.bind('<Configure>',
lambda e, sc = self._scrolledCanvas:
sc.resizescrollregion())
self.interior().bind('<Destroy>', self.onDestroy)
# Create the contents
self._treeItem = SceneGraphExplorerItem(self.nodePath)
self._node = TreeNode(self._canvas, None, self._treeItem,
DEFAULT_MENU_ITEMS + self['menuItems'])
self._node.expand()
self._parentFrame = Frame(interior)
self._label = self.createcomponent(
'parentLabel',
(), None,
Label, (interior,),
text = 'Active Reparent Target: ',
anchor = Tkinter.W, justify = Tkinter.LEFT)
self._label.pack(fill = Tkinter.X)
# Add update parent label
def updateLabel(nodePath = None, s = self):
s._label['text'] = 'Active Reparent Target: ' + nodePath.getName()
self.accept('DIRECT_activeParent', updateLabel)
# Add update hook
self.accept('SGE_Update Explorer',
lambda np, s = self: s.update())
# Check keywords and initialise options based on input values.
self.initialiseoptions(seSceneGraphExplorer)
def update(self):
""" Refresh scene graph explorer """
self._node.update()
def mouse2Down(self, event):
self._width = 1.0 * self._canvas.winfo_width()
self._height = 1.0 * self._canvas.winfo_height()
xview = self._canvas.xview()
yview = self._canvas.yview()
self._left = xview[0]
self._top = yview[0]
self._dxview = xview[1] - xview[0]
self._dyview = yview[1] - yview[0]
self._2lx = event.x
self._2ly = event.y
def mouse2Motion(self,event):
newx = self._left - ((event.x - self._2lx)/self._width) * self._dxview
self._canvas.xview_moveto(newx)
newy = self._top - ((event.y - self._2ly)/self._height) * self._dyview
self._canvas.yview_moveto(newy)
self._2lx = event.x
self._2ly = event.y
self._left = self._canvas.xview()[0]
self._top = self._canvas.yview()[0]
def onDestroy(self, event):
# Remove hooks
self.ignore('DIRECT_activeParent')
self.ignore('SGE_Update Explorer')
def deSelectTree(self):
self._node.deselecttree()
def selectNodePath(self,nodePath, callBack=True):
item = self._node.find(nodePath.id())
if item!= None:
item.select(callBack)
else:
print '----SGE: Error Selection'
class SceneGraphExplorerItem(TreeItem):
"""Example TreeItem subclass -- browse the file system."""
def __init__(self, nodePath):
self.nodePath = nodePath
def GetText(self):
type = self.nodePath.node().getType().getName()
name = self.nodePath.getName()
return type + " " + name
def GetTextForEdit(self):
name = self.nodePath.getName()
return name
def GetKey(self):
return self.nodePath.id()
def IsEditable(self):
# All nodes' names can be edited nowadays.
return 1
#return issubclass(self.nodePath.node().__class__, NamedNode)
def SetText(self, text):
try:
messenger.send('SGE_changeName', [self.nodePath, text])
except AttributeError:
pass
def GetIconName(self):
return "sphere2" # XXX wish there was a "file" icon
def IsExpandable(self):
return self.nodePath.getNumChildren() != 0
def GetSubList(self):
sublist = []
for nodePath in self.nodePath.getChildren():
item = SceneGraphExplorerItem(nodePath)
sublist.append(item)
return sublist
def OnSelect(self, callback):
messenger.send('SGE_Flash', [self.nodePath])
if not callback:
messenger.send('SGE_madeSelection', [self.nodePath, callback])
else:
messenger.send('SGE_madeSelection', [self.nodePath])
def MenuCommand(self, command):
messenger.send('SGE_' + command, [self.nodePath])
def explore(nodePath = render):
tl = Toplevel()
tl.title('Explore: ' + nodePath.getName())
sge = seSceneGraphExplorer(parent = tl, nodePath = nodePath)
sge.pack(expand = 1, fill = 'both')
return sge

View file

@ -0,0 +1,715 @@
#################################################################
# seSelection.py
# Originally from DirectSelection.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We didn't change anything essential.
# Just because we customized the seSession from DirectSession,
# So we need related files can follow the change.
# However, we don't want to change anything inside the original directool
# to let them can work with our scene editor.
# (If we do change original directools, it will force user has to install the latest version of OUR Panda)
#
#################################################################
from pandac.PandaModules import GeomNode
from direct.directtools.DirectGlobals import *
from direct.directtools.DirectUtil import *
from seGeometry import *
from direct.showbase.DirectObject import *
from quad import *
COA_ORIGIN = 0
COA_CENTER = 1
# MRM: To do: handle broken node paths in selected and deselected dicts
class DirectNodePath(NodePath):
# A node path augmented with info, bounding box, and utility methods
def __init__(self, nodePath):
# Initialize the superclass
NodePath.__init__(self)
self.assign(nodePath)
# Create a bounding box
self.bbox = DirectBoundingBox(self)
center = self.bbox.getCenter()
# Create matrix to hold the offset between the nodepath
# and its center of action (COA)
self.mCoa2Dnp = Mat4(Mat4.identMat())
if SEditor.coaMode == COA_CENTER:
self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1))
# Transform from nodePath to widget
self.tDnp2Widget = TransformState.makeIdentity()
def highlight(self):
self.bbox.show()
def dehighlight(self):
self.bbox.hide()
def getCenter(self):
return self.bbox.getCenter()
def getRadius(self):
return self.bbox.getRadius()
def getMin(self):
return self.bbox.getMin()
def getMax(self):
return self.bbox.getMax()
class SelectedNodePaths(DirectObject):
def __init__(self):
self.reset()
def reset(self):
self.selectedDict = {}
self.deselectedDict = {}
__builtins__["last"] = self.last = None
def select(self, nodePath, fMultiSelect = 0):
""" Select the specified node path. Multiselect as required """
# Do nothing if nothing selected
if not nodePath:
print 'Nothing selected!!'
return None
# Reset selected objects and highlight if multiSelect is false
if not fMultiSelect:
self.deselectAll()
# Get this pointer
id = nodePath.id()
# First see if its already in the selected dictionary
dnp = self.getSelectedDict(id)
# If so, we're done
if not dnp:
# See if it is in the deselected dictionary
dnp = self.getDeselectedDict(id)
if dnp:
# Remove it from the deselected dictionary
del(self.deselectedDict[id])
# Show its bounding box
dnp.highlight()
else:
# Didn't find it, create a new selectedNodePath instance
dnp = DirectNodePath(nodePath)
# Show its bounding box
dnp.highlight()
# Add it to the selected dictionary
self.selectedDict[dnp.id()] = dnp
# And update last
__builtins__["last"] = self.last = dnp
return dnp
def deselect(self, nodePath):
""" Deselect the specified node path """
# Get this pointer
id = nodePath.id()
# See if it is in the selected dictionary
dnp = self.getSelectedDict(id)
if dnp:
# It was selected:
# Hide its bounding box
dnp.dehighlight()
# Remove it from the selected dictionary
del(self.selectedDict[id])
# And keep track of it in the deselected dictionary
self.deselectedDict[id] = dnp
# Send a message
messenger.send('DIRECT_deselectedNodePath', [dnp])
return dnp
def getSelectedAsList(self):
"""
Return a list of all selected node paths. No verification of
connectivity is performed on the members of the list
"""
return self.selectedDict.values()[:]
def __getitem__(self,index):
return self.getSelectedAsList()[index]
def getSelectedDict(self, id):
"""
Search selectedDict for node path, try to repair broken node paths.
"""
dnp = self.selectedDict.get(id, None)
if dnp:
return dnp
else:
# Not in selected dictionary
return None
def getDeselectedAsList(self):
return self.deselectedDict.values()[:]
def getDeselectedDict(self, id):
"""
Search deselectedDict for node path, try to repair broken node paths.
"""
dnp = self.deselectedDict.get(id, None)
if dnp:
# Yes
return dnp
else:
# Not in deselected dictionary
return None
def forEachSelectedNodePathDo(self, func):
"""
Perform given func on selected node paths. No node path
connectivity verification performed
"""
selectedNodePaths = self.getSelectedAsList()
for nodePath in selectedNodePaths:
func(nodePath)
def forEachDeselectedNodePathDo(self, func):
"""
Perform given func on deselected node paths. No node path
connectivity verification performed
"""
deselectedNodePaths = self.getDeselectedAsList()
for nodePath in deselectedNodePaths:
func(nodePath)
def getWrtAll(self):
self.forEachSelectedNodePathDo(self.getWrt)
def getWrt(self, nodePath):
nodePath.tDnp2Widget = nodePath.getTransform(SEditor.widget)
def moveWrtWidgetAll(self):
self.forEachSelectedNodePathDo(self.moveWrtWidget)
def moveWrtWidget(self, nodePath):
nodePath.setTransform(SEditor.widget, nodePath.tDnp2Widget)
def deselectAll(self):
self.forEachSelectedNodePathDo(self.deselect)
def highlightAll(self):
self.forEachSelectedNodePathDo(DirectNodePath.highlight)
def dehighlightAll(self):
self.forEachSelectedNodePathDo(DirectNodePath.dehighlight)
def removeSelected(self):
selected = self.last
if selected:
selected.remove()
__builtins__["last"] = self.last = None
def removeAll(self):
# Remove all selected nodePaths from the Scene Graph
self.forEachSelectedNodePathDo(NodePath.remove)
def toggleVisSelected(self):
selected = self.last
# Toggle visibility of selected node paths
if selected:
selected.toggleVis()
def toggleVisAll(self):
# Toggle viz for all selected node paths
self.forEachSelectedNodePathDo(NodePath.toggleVis)
def isolateSelected(self):
selected = self.last
if selected:
selected.isolate()
def getDirectNodePath(self, nodePath):
# Get this pointer
id = nodePath.id()
# First check selected dict
dnp = self.getSelectedDict(id)
if dnp:
return dnp
# Otherwise return result of deselected search
return self.getDeselectedDict(id)
def getNumSelected(self):
return len(self.selectedDict.keys())
class DirectBoundingBox:
def __init__(self, nodePath):
# Record the node path
self.nodePath = nodePath
# Compute bounds, min, max, etc.
self.computeTightBounds()
# Generate the bounding box
self.lines = self.createBBoxLines()
def computeTightBounds(self):
# Compute bounding box using tighter calcTightBounds function
# Need to clear out existing transform on node path
tMat = Mat4()
tMat.assign(self.nodePath.getMat())
self.nodePath.clearMat()
# Get bounds
self.min = Point3(0)
self.max = Point3(0)
self.nodePath.calcTightBounds(self.min,self.max)
# Calc center and radius
self.center = Point3((self.min + self.max)/2.0)
self.radius = Vec3(self.max - self.min).length()
# Restore transform
self.nodePath.setMat(tMat)
del tMat
def computeBounds(self):
self.bounds = self.getBounds()
if self.bounds.isEmpty() or self.bounds.isInfinite():
self.center = Point3(0)
self.radius = 1.0
else:
self.center = self.bounds.getCenter()
self.radius = self.bounds.getRadius()
self.min = Point3(self.center - Point3(self.radius))
self.max = Point3(self.center + Point3(self.radius))
def createBBoxLines(self):
# Create a line segments object for the bbox
lines = LineNodePath(hidden)
lines.node().setName('bboxLines')
lines.setColor( VBase4( 1., 0., 0., 1. ) )
lines.setThickness( 0.5 )
minX = self.min[0]
minY = self.min[1]
minZ = self.min[2]
maxX = self.max[0]
maxY = self.max[1]
maxZ = self.max[2]
# Bottom face
lines.moveTo( minX, minY, minZ )
lines.drawTo( maxX, minY, minZ )
lines.drawTo( maxX, maxY, minZ )
lines.drawTo( minX, maxY, minZ )
lines.drawTo( minX, minY, minZ )
# Front Edge/Top face
lines.drawTo( minX, minY, maxZ )
lines.drawTo( maxX, minY, maxZ )
lines.drawTo( maxX, maxY, maxZ )
lines.drawTo( minX, maxY, maxZ )
lines.drawTo( minX, minY, maxZ )
# Three remaining edges
lines.moveTo( maxX, minY, minZ )
lines.drawTo( maxX, minY, maxZ )
lines.moveTo( maxX, maxY, minZ )
lines.drawTo( maxX, maxY, maxZ )
lines.moveTo( minX, maxY, minZ )
lines.drawTo( minX, maxY, maxZ )
# Create and return bbox lines
lines.create()
# Make sure bbox is never lit or drawn in wireframe
useDirectRenderStyle(lines)
return lines
def updateBBoxLines(self):
ls = self.lines.lineSegs
minX = self.min[0]
minY = self.min[1]
minZ = self.min[2]
maxX = self.max[0]
maxY = self.max[1]
maxZ = self.max[2]
# Bottom face
ls.setVertex( 0, minX, minY, minZ )
ls.setVertex( 1, maxX, minY, minZ )
ls.setVertex( 2, maxX, maxY, minZ )
ls.setVertex( 3, minX, maxY, minZ )
ls.setVertex( 4, minX, minY, minZ )
# Front Edge/Top face
ls.setVertex( 5, minX, minY, maxZ )
ls.setVertex( 6, maxX, minY, maxZ )
ls.setVertex( 7, maxX, maxY, maxZ )
ls.setVertex( 8, minX, maxY, maxZ )
ls.setVertex( 9, minX, minY, maxZ )
# Three remaining edges
ls.setVertex( 10, maxX, minY, minZ )
ls.setVertex( 11, maxX, minY, maxZ )
ls.setVertex( 12, maxX, maxY, minZ )
ls.setVertex( 13, maxX, maxY, maxZ )
ls.setVertex( 14, minX, maxY, minZ )
ls.setVertex( 15, minX, maxY, maxZ )
def getBounds(self):
# Get a node path's bounds
nodeBounds = BoundingSphere()
nodeBounds.extendBy(self.nodePath.node().getInternalBound())
for child in self.nodePath.getChildren():
nodeBounds.extendBy(child.getBounds())
return nodeBounds.makeCopy()
def show(self):
self.lines.reparentTo(self.nodePath)
def hide(self):
self.lines.reparentTo(hidden)
def getCenter(self):
return self.center
def getRadius(self):
return self.radius
def getMin(self):
return self.min
def getMax(self):
return self.max
def vecAsString(self, vec):
return '%.2f %.2f %.2f' % (vec[0], vec[1], vec[2])
def __repr__(self):
return (`self.__class__` +
'\nNodePath:\t%s\n' % self.nodePath.getName() +
'Min:\t\t%s\n' % self.vecAsString(self.min) +
'Max:\t\t%s\n' % self.vecAsString(self.max) +
'Center:\t\t%s\n' % self.vecAsString(self.center) +
'Radius:\t\t%.2f' % self.radius
)
class SelectionQueue(CollisionHandlerQueue):
def __init__(self, parentNP = render):
# Initialize the superclass
CollisionHandlerQueue.__init__(self)
# Current index and entry in collision queue
self.index = -1
self.entry = None
self.skipFlags = SKIP_NONE
# Create a collision node path attached to the given NP
self.collisionNodePath = NodePath(CollisionNode("collisionNP"))
self.setParentNP(parentNP)
# Don't pay the penalty of drawing this collision ray
self.collisionNodePath.hide()
self.collisionNode = self.collisionNodePath.node()
# Intersect with geometry to begin with
self.collideWithGeom()
# And a traverser to do the actual collision tests
self.ct = CollisionTraverser()
# Let the traverser know about the collision node and the queue
#Manakel 2/12/2005: replace CollisionNode by its nodepath
self.ct.addCollider(self.collisionNodePath, self)
# List of objects that can't be selected
self.unpickable = UNPICKABLE
# Derived class must add Collider to complete initialization
def setParentNP(self, parentNP):
# Update collisionNodePath's parent
self.collisionNodePath.reparentTo(parentNP)
def addCollider(self, collider):
# Inherited class must call this function to specify collider object
# Record collision object
self.collider = collider
# Add the collider to the collision Node
self.collisionNode.addSolid( self.collider )
def collideWithBitMask(self, bitMask):
# The into collide mask is the bit pattern colliders look at
# when deciding whether or not to test for a collision "into"
# this collision solid. Set to all Off so this collision solid
# will not be considered in any collision tests
self.collisionNode.setIntoCollideMask(BitMask32().allOff())
# The from collide mask is the bit pattern *this* collision solid
# compares against the into collide mask of candidate collision solids
# Turn this mask all off since we're not testing for collisions against
# collision solids
self.collisionNode.setFromCollideMask(bitMask)
def collideWithGeom(self):
# The into collide mask is the bit pattern colliders look at
# when deciding whether or not to test for a collision "into"
# this collision solid. Set to all Off so this collision solid
# will not be considered in any collision tests
self.collisionNode.setIntoCollideMask(BitMask32().allOff())
# The from collide mask is the bit pattern *this* collision solid
# compares against the into collide mask of candidate collision solids
# Turn this mask all off since we're not testing for collisions against
# collision solids, but we do want to test against geometry
self.collisionNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
def collideWithWidget(self):
# This collision node should not be tested against by any other
# collision solids
self.collisionNode.setIntoCollideMask(BitMask32().allOff())
# This collision node will test for collisions with any collision
# solids with a bit mask set to 0x80000000
mask = BitMask32()
mask.setBit(31)
self.collisionNode.setFromCollideMask(mask)
def addUnpickable(self, item):
if item not in self.unpickable:
self.unpickable.append(item)
def removeUnpickable(self, item):
if item in self.unpickable:
self.unpickable.remove(item)
def setCurrentIndex(self, index):
if (index < 0) or (index >= self.getNumEntries()):
self.index = -1
else:
self.index = index
def setCurrentEntry(self, entry):
self.entry = entry
def getCurrentEntry(self):
return self.entry
def isEntryBackfacing(self, entry):
# If dot product of collision point surface normal and
# ray from camera to collision point is positive, we are
# looking at the backface of the polygon
if not entry.hasSurfaceNormal():
# Well, no way to tell. Assume we're not backfacing.
return 0
fromNodePath = entry.getFromNodePath()
v = Vec3(entry.getSurfacePoint(fromNodePath))
n = entry.getSurfaceNormal(fromNodePath)
# Convert to camera space for backfacing test
if self.collisionNodePath.getParent() != base.cam:
# Problem: assumes base.cam is the camera in question
p2cam = self.collisionNodePath.getParent().getMat(base.cam)
v = Vec3(p2cam.xformPoint(v))
n = p2cam.xformVec(n)
# Normalize and check angle between to vectors
v.normalize()
return v.dot(n) >= 0
def findNextCollisionEntry(self, skipFlags = SKIP_NONE):
return self.findCollisionEntry(skipFlags, self.index + 1)
def findCollisionEntry(self, skipFlags = SKIP_NONE, startIndex = 0 ):
# Init self.index and self.entry
self.setCurrentIndex(-1)
self.setCurrentEntry(None)
# Pick out the closest object that isn't a widget
for i in range(startIndex,self.getNumEntries()):
entry = self.getEntry(i)
nodePath = entry.getIntoNodePath()
if (skipFlags & SKIP_HIDDEN) and nodePath.isHidden():
# Skip if hidden node
pass
elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
# Skip, if backfacing poly
pass
elif ((skipFlags & SKIP_CAMERA) and
(camera in nodePath.getAncestors())):
# Skip if parented to a camera.
pass
# Can pick unpickable, use the first visible node
elif ((skipFlags & SKIP_UNPICKABLE) and
(nodePath.getName() in self.unpickable)):
# Skip if in unpickable list
pass
else:
self.setCurrentIndex(i)
self.setCurrentEntry(entry)
break
return self.getCurrentEntry()
class SelectionRay(SelectionQueue):
def __init__(self, parentNP = render):
# Initialize the superclass
SelectionQueue.__init__(self, parentNP)
self.addCollider(CollisionRay())
def pick(self, targetNodePath, xy = None):
# Determine ray direction based upon the mouse coordinates
if xy:
mx = xy[0]
my = xy[1]
elif direct:
mx = SEditor.dr.mouseX
my = SEditor.dr.mouseY
else:
if not base.mouseWatcherNode.hasMouse():
# No mouse in window.
self.clearEntries()
return
mx = base.mouseWatcherNode.getMouseX()
my = base.mouseWatcherNode.getMouseY()
#base.mouseWatcherNode.setDisplayRegion(base.win.getDisplayRegion(0))
#mx = base.mouseWatcherNode.getMouseX()+1
#my = base.mouseWatcherNode.getMouseY()+1
#print base.camNode.getName()
#print "Arrived X" + str(mx) + " Arrived Y " + str(my)
self.collider.setFromLens( base.camNode, mx, my )
self.ct.traverse( targetNodePath )
self.sortEntries()
def pickBitMask(self, bitMask = BitMask32.allOff(),
targetNodePath = render,
skipFlags = SKIP_ALL ):
self.collideWithBitMask(bitMask)
self.pick(targetNodePath)
# Determine collision entry
return self.findCollisionEntry(skipFlags)
def pickGeom(self, targetNodePath = render, skipFlags = SKIP_ALL,
xy = None):
self.collideWithGeom()
self.pick(targetNodePath, xy = xy)
# Determine collision entry
return self.findCollisionEntry(skipFlags)
def pickWidget(self, targetNodePath = render, skipFlags = SKIP_NONE ):
self.collideWithWidget()
self.pick(targetNodePath)
# Determine collision entry
return self.findCollisionEntry(skipFlags)
def pick3D(self, targetNodePath, origin, dir):
# Determine ray direction based upon the mouse coordinates
self.collider.setOrigin( origin )
self.collider.setDirection( dir )
self.ct.traverse( targetNodePath )
self.sortEntries()
def pickGeom3D(self, targetNodePath = render,
origin = Point3(0), dir = Vec3(0,0,-1),
skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
self.collideWithGeom()
self.pick3D(targetNodePath, origin, dir)
# Determine collision entry
return self.findCollisionEntry(skipFlags)
def pickBitMask3D(self, bitMask = BitMask32.allOff(),
targetNodePath = render,
origin = Point3(0), dir = Vec3(0,0,-1),
skipFlags = SKIP_ALL ):
self.collideWithBitMask(bitMask)
self.pick3D(targetNodePath, origin, dir)
# Determine collision entry
return self.findCollisionEntry(skipFlags)
class SelectionSegment(SelectionQueue):
# Like a selection ray but with two endpoints instead of an endpoint
# and a direction
def __init__(self, parentNP = render, numSegments = 1):
# Initialize the superclass
SelectionQueue.__init__(self, parentNP)
self.colliders = []
self.numColliders = 0
for i in range(numSegments):
self.addCollider(CollisionSegment())
def addCollider(self, collider):
# Record new collision object
self.colliders.append(collider)
# Add the collider to the collision Node
self.collisionNode.addSolid( collider )
self.numColliders += 1
def pickGeom(self, targetNodePath = render, endPointList = [],
skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
self.collideWithGeom()
for i in range(min(len(endPointList), self.numColliders)):
pointA, pointB = endPointList[i]
collider = self.colliders[i]
collider.setPointA( pointA )
collider.setPointB( pointB )
self.ct.traverse( targetNodePath )
# Determine collision entry
return self.findCollisionEntry(skipFlags)
def pickBitMask(self, bitMask = BitMask32.allOff(),
targetNodePath = render, endPointList = [],
skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
self.collideWithBitMask(bitMask)
for i in range(min(len(endPointList), self.numColliders)):
pointA, pointB = endPointList[i]
collider = self.colliders[i]
collider.setPointA( pointA )
collider.setPointB( pointB )
self.ct.traverse( targetNodePath )
# Determine collision entry
return self.findCollisionEntry(skipFlags)
class SelectionSphere(SelectionQueue):
# Wrapper around collision sphere
def __init__(self, parentNP = render, numSpheres = 1):
# Initialize the superclass
SelectionQueue.__init__(self, parentNP)
self.colliders = []
self.numColliders = 0
for i in range(numSpheres):
self.addCollider(CollisionSphere(Point3(0), 1))
def addCollider(self, collider):
# Record new collision object
self.colliders.append(collider)
# Add the collider to the collision Node
self.collisionNode.addSolid( collider )
self.numColliders += 1
def setCenter(self, i, center):
c = self.colliders[i]
c.setCenter(center)
def setRadius(self, i, radius):
c = self.colliders[i]
c.setRadius(radius)
def setCenterRadius(self, i, center, radius):
c = self.colliders[i]
c.setCenter(center)
c.setRadius(radius)
def isEntryBackfacing(self, entry):
# If dot product of collision point surface normal and
# ray from sphere origin to collision point is positive,
# center is on the backside of the polygon
fromNodePath = entry.getFromNodePath()
v = Vec3(entry.getSurfacePoint(fromNodePath) -
entry.getFrom().getCenter())
n = entry.getSurfaceNormal(fromNodePath)
# If points almost on top of each other, reject face
# (treat as backfacing)
if v.length() < 0.05:
return 1
# Normalize and check angle between to vectors
v.normalize()
return v.dot(n) >= 0
def pick(self, targetNodePath, skipFlags):
self.ct.traverse( targetNodePath )
self.sortEntries()
return self.findCollisionEntry(skipFlags)
def pickGeom(self, targetNodePath = render,
skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
self.collideWithGeom()
return self.pick(targetNodePath, skipFlags)
def pickBitMask(self, bitMask = BitMask32.allOff(),
targetNodePath = render,
skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
self.collideWithBitMask(bitMask)
return self.pick(targetNodePath, skipFlags)

View file

@ -0,0 +1,991 @@
#################################################################
# seSession.py
# Originally from DirectSession.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# We took out a lot of stuff we don't need in the sceneeditor.
# This is also the main reason we didn't just inherite the original directSession.
# Also, the way of selecting, renaming and some hot-key controls are changed.
#
#################################################################
from direct.showbase.DirectObject import *
from direct.directtools.DirectGlobals import *
from direct.directtools.DirectUtil import*
from direct.interval.IntervalGlobal import *
from seCameraControl import *
from seManipulation import *
from seSelection import *
from seGrid import *
from seGeometry import *
from direct.tkpanels import Placer
from direct.tkwidgets import Slider
from direct.gui import OnscreenText
import types
import string
from direct.showbase import Loader
class SeSession(DirectObject): ### Customized DirectSession
def __init__(self):
# Establish a global pointer to the direct object early on
# so dependant classes can access it in their code
__builtins__["SEditor"] = self
# These come early since they are used later on
self.group = render.attachNewNode('SEditor')
self.font = TextNode.getDefaultFont()
self.fEnabled = 0
self.drList = DisplayRegionList()
self.iRayList = map(lambda x: x.iRay, self.drList)
self.dr = self.drList[0]
self.camera = base.camera
self.trueCamera = self.camera
self.iRay = self.dr.iRay
self.coaMode = COA_ORIGIN
self.enableAutoCamera = True
self.cameraControl = DirectCameraControl()
self.manipulationControl = DirectManipulationControl()
self.useObjectHandles()
self.grid = DirectGrid()
self.grid.disable()
# Initialize the collection of selected nodePaths
self.selected = SelectedNodePaths()
# Ancestry of currently selected object
self.ancestry = []
self.ancestryIndex = 0
self.activeParent = None
self.selectedNPReadout = OnscreenText.OnscreenText(
pos = (-1.0, -0.9), bg=Vec4(1,1,1,1),
scale = 0.05, align = TextNode.ALeft,
mayChange = 1, font = self.font)
# Make sure readout is never lit or drawn in wireframe
useDirectRenderStyle(self.selectedNPReadout)
self.selectedNPReadout.reparentTo(hidden)
self.activeParentReadout = OnscreenText.OnscreenText(
pos = (-1.0, -0.975), bg=Vec4(1,1,1,1),
scale = 0.05, align = TextNode.ALeft,
mayChange = 1, font = self.font)
# Make sure readout is never lit or drawn in wireframe
useDirectRenderStyle(self.activeParentReadout)
self.activeParentReadout.reparentTo(hidden)
self.directMessageReadout = OnscreenText.OnscreenText(
pos = (-1.0, 0.9), bg=Vec4(1,1,1,1),
scale = 0.05, align = TextNode.ALeft,
mayChange = 1, font = self.font)
# Make sure readout is never lit or drawn in wireframe
useDirectRenderStyle(self.directMessageReadout)
self.directMessageReadout.reparentTo(hidden)
self.fControl = 0
self.fAlt = 0
self.fShift = 0
self.pos = VBase3()
self.hpr = VBase3()
self.scale = VBase3()
self.hitPt = Point3(0.0)
# Lists for managing undo/redo operations
self.undoList = []
self.redoList = []
# One run through the context task to init everything
self.drList.updateContext()
for dr in self.drList:
dr.camUpdate()
self.modifierEvents = ['control', 'control-up',
'shift', 'shift-up',
'alt', 'alt-up',
]
self.keyEvents = ['escape', 'delete', 'page_up', 'page_down',
'[', '{', ']', '}',
'shift-a', 'b', 'control-f',
'l', 'shift-l', 'o', 'p', 'r',
'shift-r', 's', 't', 'v', 'w']
self.mouseEvents = ['mouse1', 'mouse1-up',
'shift-mouse1', 'shift-mouse1-up',
'control-mouse1', 'control-mouse1-up',
'alt-mouse1', 'alt-mouse1-up',
'mouse2', 'mouse2-up',
'shift-mouse2', 'shift-mouse2-up',
'control-mouse2', 'control-mouse2-up',
'alt-mouse2', 'alt-mouse2-up',
'mouse3', 'mouse3-up',
'shift-mouse3', 'shift-mouse3-up',
'control-mouse3', 'control-mouse3-up',
'alt-mouse3', 'alt-mouse3-up',
]
def enable(self):
if self.fEnabled:
return
# Make sure old tasks are shut down
self.disable()
# Start all display region context tasks
self.drList.spawnContextTask()
# Turn on mouse Flying
self.cameraControl.enableMouseFly()
# Turn on object manipulation
self.manipulationControl.enableManipulation()
# Make sure list of selected items is reset
self.selected.reset()
# Accept appropriate hooks
self.enableKeyEvents()
self.enableModifierEvents()
self.enableMouseEvents()
# Set flag
self.fEnabled = 1
if self.enableAutoCamera:
self.accept('DH_LoadingComplete', self.autoCameraMove)
def disable(self):
# Shut down all display region context tasks
self.drList.removeContextTask()
# Turn off camera fly
self.cameraControl.disableMouseFly()
# Turn off object manipulation
self.manipulationControl.disableManipulation()
self.disableKeyEvents()
self.disableModifierEvents()
self.disableMouseEvents()
self.ignore('DH_LoadingComplete')
# Kill tasks
taskMgr.remove('flashNodePath')
taskMgr.remove('hideDirectMessage')
taskMgr.remove('hideDirectMessageLater')
# Set flag
self.fEnabled = 0
def minimumConfiguration(self):
# Remove context task
self.drList.removeContextTask()
# Turn off camera fly
self.cameraControl.disableMouseFly()
# Ignore keyboard and action events
self.disableKeyEvents()
self.disableActionEvents()
# But let mouse events pass through
self.enableMouseEvents()
self.enableModifierEvents()
def oobe(self):
# If oobeMode was never set, set it to false and create the
# structures we need to implement OOBE.
try:
self.oobeMode
except:
self.oobeMode = 0
self.oobeCamera = hidden.attachNewNode('oobeCamera')
self.oobeVis = loader.loadModelOnce('models/misc/camera')
if self.oobeVis:
self.oobeVis.node().setFinal(1)
if self.oobeMode:
# Position a target point to lerp the oobe camera to
self.cameraControl.camManipRef.iPosHpr(self.trueCamera)
t = self.oobeCamera.lerpPosHpr(
Point3(0), Vec3(0), 2.0,
other = self.cameraControl.camManipRef,
task = 'manipulateCamera',
blendType = 'easeInOut')
# When move is done, switch to oobe mode
t.uponDeath = self.endOOBE
else:
# Place camera marker at true camera location
self.oobeVis.reparentTo(self.trueCamera)
# Remove any transformation on the models arc
self.oobeVis.clearMat()
# Make oobeCamera be a sibling of wherever camera is now.
cameraParent = self.camera.getParent()
# Prepare oobe camera
self.oobeCamera.reparentTo(cameraParent)
self.oobeCamera.iPosHpr(self.trueCamera)
# Put camera under new oobe camera
base.cam.reparentTo(self.oobeCamera)
# Position a target point to lerp the oobe camera to
self.cameraControl.camManipRef.setPos(
self.trueCamera, Vec3(-2,-20, 5))
self.cameraControl.camManipRef.lookAt(self.trueCamera)
t = self.oobeCamera.lerpPosHpr(
Point3(0), Vec3(0), 2.0,
other = self.cameraControl.camManipRef,
task = 'manipulateCamera',
blendType = 'easeInOut')
# When move is done, switch to oobe mode
t.uponDeath = self.beginOOBE
def beginOOBE(self, state):
# Make sure we've reached our final destination
self.oobeCamera.iPosHpr(self.cameraControl.camManipRef)
self.camera = self.oobeCamera
self.oobeMode = 1
def endOOBE(self, state):
# Make sure we've reached our final destination
self.oobeCamera.iPosHpr(self.trueCamera)
# Disable OOBE mode.
base.cam.reparentTo(self.trueCamera)
self.camera = self.trueCamera
# Get rid of ancillary node paths
self.oobeVis.reparentTo(hidden)
self.oobeCamera.reparentTo(hidden)
self.oobeMode = 0
def destroy(self):
self.disable()
def reset(self):
self.enable()
# EVENT FUNCTIONS
def enableModifierEvents(self):
for event in self.modifierEvents:
self.accept(event, self.inputHandler, [event])
def enableKeyEvents(self):
for event in self.keyEvents:
self.accept(event, self.inputHandler, [event])
def enableMouseEvents(self):
for event in self.mouseEvents:
self.accept(event, self.inputHandler, [event])
def disableModifierEvents(self):
for event in self.modifierEvents:
self.ignore(event)
def disableKeyEvents(self):
for event in self.keyEvents:
self.ignore(event)
def disableMouseEvents(self):
for event in self.mouseEvents:
self.ignore(event)
def inputHandler(self, input):
# Deal with keyboard and mouse input
if input == 'mouse1-up':
messenger.send('DIRECT-mouse1Up')
if SEditor.widget.fActive:
messenger.send('shift-f')
elif input.find('mouse1') != -1:
modifiers = self.getModifiers(input, 'mouse1')
messenger.send('DIRECT-mouse1', sentArgs = [modifiers])
elif input == 'mouse2-up':
messenger.send('DIRECT-mouse2Up')
if SEditor.widget.fActive:
messenger.send('shift-f')
elif input.find('mouse2') != -1:
modifiers = self.getModifiers(input, 'mouse2')
messenger.send('DIRECT-mouse2', sentArgs = [modifiers])
elif input == 'mouse3-up':
messenger.send('DIRECT-mouse3Up')
if SEditor.widget.fActive:
messenger.send('shift-f')
elif input.find('mouse3') != -1:
modifiers = self.getModifiers(input, 'mouse3')
messenger.send('DIRECT-mouse3', sentArgs = [modifiers])
elif input == 'shift':
self.fShift = 1
elif input == 'shift-up':
self.fShift = 0
elif input == 'control':
self.fControl = 1
elif input == 'control-up':
self.fControl = 0
elif input == 'alt':
self.fAlt = 1
elif input == 'alt-up':
self.fAlt = 0
elif input == 'page_up':
self.upAncestry()
elif input == 'page_down':
self.downAncestry()
elif input == 'escape':
self.deselectAll()
elif input == 'delete':
taskMgr.remove('followSelectedNodePath')
#self.removeAllSelected()
messenger.send('SGE_Remove',[None])
self.deselectAll()
elif input == 'v':
messenger.send('SEditor-ToggleWidgetVis')
self.toggleWidgetVis()
if SEditor.widget.fActive:
messenger.send('shift-f')
elif input == 'b':
messenger.send('SEditor-ToggleBackface')
base.toggleBackface()
#elif input == 'control-f':
# self.flash(last)
elif input == 'shift-l':
self.cameraControl.toggleCOALock()
elif input == 'o':
self.oobe()
elif input == 'p':
if self.selected.last:
self.setActiveParent(self.selected.last)
elif input == 'r':
# Do wrt reparent
if self.selected.last:
self.reparent(self.selected.last, fWrt = 1)
elif input == 'shift-r':
# Do regular reparent
if self.selected.last:
self.reparent(self.selected.last)
elif input == 's':
if self.selected.last:
self.select(self.selected.last)
elif input == 't':
messenger.send('SEditor-ToggleTexture')
base.toggleTexture()
elif input == 'shift-a':
self.selected.toggleVisAll()
elif input == 'w':
messenger.send('SEditor-ToggleWireframe')
base.toggleWireframe()
elif (input == '[') or (input == '{'):
self.undo()
elif (input == ']') or (input == '}'):
self.redo()
def getModifiers(self, input, base):
modifiers = DIRECT_NO_MOD
modifierString = input[: input.find(base)]
if modifierString.find('shift') != -1:
modifiers |= DIRECT_SHIFT_MOD
if modifierString.find('control') != -1:
modifiers |= DIRECT_CONTROL_MOD
if modifierString.find('alt') != -1:
modifiers |= DIRECT_ALT_MOD
return modifiers
def gotShift(self, modifiers):
return modifiers & DIRECT_SHIFT_MOD
def gotControl(self, modifiers):
return modifiers & DIRECT_CONTROL_MOD
def gotAlt(self, modifiers):
return modifiers & DIRECT_ALT_MOD
def select(self, nodePath, fMultiSelect = 0, fResetAncestry = 1, callback=False):
dnp = self.selected.select(nodePath, fMultiSelect)
if dnp:
messenger.send('DIRECT_preSelectNodePath', [dnp])
if fResetAncestry:
# Update ancestry
self.ancestry = dnp.getAncestors()
self.ancestry.reverse()
self.ancestryIndex = 0
# Update the selectedNPReadout
self.selectedNPReadout.reparentTo(aspect2d)
self.selectedNPReadout.setText(
'Selected:' + dnp.getName())
# Show the manipulation widget
self.widget.showWidget()
# Update camera controls coa to this point
# Coa2Camera = Coa2Dnp * Dnp2Camera
mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera)
row = mCoa2Camera.getRow(3)
coa = Vec3(row[0], row[1], row[2])
self.cameraControl.updateCoa(coa)
# Adjust widgets size
# This uses the additional scaling factor used to grow and
# shrink the widget
self.widget.setScalingFactor(dnp.getRadius())
# Spawn task to have object handles follow the selected object
taskMgr.remove('followSelectedNodePath')
t = Task.Task(self.followSelectedNodePathTask)
t.dnp = dnp
taskMgr.add(t, 'followSelectedNodePath')
# Send an message marking the event
messenger.send('DIRECT_selectedNodePath', [dnp])
if callback:
messenger.send('se_selectedNodePath', [dnp, False])
else:
messenger.send('se_selectedNodePath', [dnp])
self.upAncestry()
if SEditor.widget.fActive:
messenger.send('shift-f')
def followSelectedNodePathTask(self, state):
mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(render)
decomposeMatrix(mCoa2Render,
self.scale,self.hpr,self.pos,
CSDefault)
self.widget.setPosHpr(self.pos,self.hpr)
return Task.cont
def deselect(self, nodePath):
dnp = self.selected.deselect(nodePath)
if dnp:
# Hide the manipulation widget
self.widget.hideWidget()
self.selectedNPReadout.reparentTo(hidden)
self.selectedNPReadout.setText(' ')
taskMgr.remove('followSelectedNodePath')
self.ancestry = []
# Send an message marking the event
messenger.send('DIRECT_deselectedNodePath', [dnp])
def deselectAll(self):
self.selected.deselectAll()
# Hide the manipulation widget
self.widget.hideWidget()
self.selectedNPReadout.reparentTo(hidden)
self.selectedNPReadout.setText(' ')
taskMgr.remove('followSelectedNodePath')
messenger.send('se_deselectedAll')
def setActiveParent(self, nodePath = None):
# Record new parent
self.activeParent = nodePath
# Update the activeParentReadout
self.activeParentReadout.reparentTo(aspect2d)
self.activeParentReadout.setText(
'Active Reparent Target:' + nodePath.getName())
# Alert everyone else
self.activeParentReadout.show()
def reparent(self, nodePath = None, fWrt = 0):
if (nodePath and self.activeParent and
self.isNotCycle(nodePath, self.activeParent)):
oldParent = nodePath.getParent()
if fWrt:
nodePath.wrtReparentTo(self.activeParent)
else:
nodePath.reparentTo(self.activeParent)
# Alert everyone else
messenger.send('DIRECT_reparent',
[nodePath, oldParent, self.activeParent])
messenger.send('SGE_Update Explorer',[render])
self.activeParentReadout.hide()
def isNotCycle(self, nodePath, parent):
if nodePath.id() == parent.id():
print 'DIRECT.reparent: Invalid parent'
return 0
elif parent.hasParent():
return self.isNotCycle(nodePath, parent.getParent())
else:
return 1
def fitOnNodePath(self, nodePath = 'None Given'):
if nodePath == 'None Given':
# If nothing specified, try selected node path
nodePath = self.selected.last
SEditor.select(nodePath)
def fitTask(state, self = self):
self.cameraControl.fitOnWidget()
return Task.done
taskMgr.doMethodLater(0.1, fitTask, 'manipulateCamera')
def isolate(self, nodePath = 'None Given'):
""" Show a node path and hide its siblings """
# First kill the flashing task to avoid complications
taskMgr.remove('flashNodePath')
# Use currently selected node path if node selected
if nodePath == 'None Given':
nodePath = self.selected.last
# Do we have a node path?
if nodePath:
# Yes, show everything in level
self.showAllDescendants(nodePath.getParent())
# Now hide all of this node path's siblings
nodePath.hideSiblings()
def toggleVis(self, nodePath = 'None Given'):
""" Toggle visibility of node path """
# First kill the flashing task to avoid complications
taskMgr.remove('flashNodePath')
if nodePath == 'None Given':
# If nothing specified, try selected node path
nodePath = self.selected.last
if nodePath:
# Now toggle node path's visibility state
nodePath.toggleVis()
def removeNodePath(self, nodePath = 'None Given'):
if nodePath == 'None Given':
# If nothing specified, try selected node path
nodePath = self.selected.last
if nodePath:
nodePath.remove()
def removeAllSelected(self):
self.selected.removeAll()
def showAllDescendants(self, nodePath = render):
""" Show the level and its descendants """
nodePath.showAllDescendants()
nodePath.hideCS()
def upAncestry(self):
if self.ancestry:
l = len(self.ancestry)
i = self.ancestryIndex + 1
if i < l:
np = self.ancestry[i]
name = np.getName()
if i>0:
type = self.ancestry[i-1].node().getType().getName()
else:
type = self.ancestry[0].node().getType().getName()
ntype = np.node().getType().getName()
if (name != 'render') and (name != 'renderTop')and(self.checkTypeNameForAncestry(type, ntype)):
self.ancestryIndex = i
self.select(np, 0, 0, True)
def checkTypeNameForAncestry(self, type, nextType ):
if (type=='ModelRoot'):
if (nextType=='AmbientLight')or(nextType=='PointLight')or(nextType=='DirectionalLight')or(nextType=='Spotlight'):
return True
return False
elif (type=='ModelNode'):
if (nextType=='ModelNode'):
return True
return False
elif (type=='CollisionNode'):
return False
elif (type=='ActorNode'):
return False
elif (type=='AmbientLight')or(type=='PointLight')or(type=='DirectionalLight')or(type=='Spotlight'):
return False
else:
return True
def downAncestry(self):
if self.ancestry:
l = len(self.ancestry)
i = self.ancestryIndex - 1
if i >= 0:
np = self.ancestry[i]
name = np.getName()
if (name != 'render') and (name != 'renderTop'):
self.ancestryIndex = i
self.select(np, 0, 0, True)
def getAndSetName(self, nodePath):
""" Prompt user for new node path name """
from tkSimpleDialog import askstring
newName = askstring('Node Path: ' + nodePath.getName(),
'Enter new name:')
if newName:
nodePath.setName(newName)
messenger.send('DIRECT_nodePathSetName', [nodePath, newName])
# UNDO REDO FUNCTIONS
def pushUndo(self, nodePathList, fResetRedo = 1):
# Assemble group of changes
undoGroup = []
for nodePath in nodePathList:
t = nodePath.getTransform()
undoGroup.append([nodePath, t])
# Now record group
self.undoList.append(undoGroup)
# Truncate list
self.undoList = self.undoList[-25:]
# Alert anyone who cares
messenger.send('DIRECT_pushUndo')
if fResetRedo and (nodePathList != []):
self.redoList = []
messenger.send('DIRECT_redoListEmpty')
def popUndoGroup(self):
# Get last item
undoGroup = self.undoList[-1]
# Strip last item off of undo list
self.undoList = self.undoList[:-1]
# Update state of undo button
if not self.undoList:
messenger.send('DIRECT_undoListEmpty')
# Return last item
return undoGroup
def pushRedo(self, nodePathList):
# Assemble group of changes
redoGroup = []
for nodePath in nodePathList:
t = nodePath.getTransform()
redoGroup.append([nodePath, t])
# Now record redo group
self.redoList.append(redoGroup)
# Truncate list
self.redoList = self.redoList[-25:]
# Alert anyone who cares
messenger.send('DIRECT_pushRedo')
def popRedoGroup(self):
# Get last item
redoGroup = self.redoList[-1]
# Strip last item off of redo list
self.redoList = self.redoList[:-1]
# Update state of redo button
if not self.redoList:
messenger.send('DIRECT_redoListEmpty')
# Return last item
return redoGroup
def undo(self):
if self.undoList:
# Get last item off of redo list
undoGroup = self.popUndoGroup()
# Record redo information
nodePathList = map(lambda x: x[0], undoGroup)
self.pushRedo(nodePathList)
# Now undo xform for group
for pose in undoGroup:
# Undo xform
pose[0].setTransform(pose[1])
# Alert anyone who cares
messenger.send('DIRECT_undo')
def redo(self):
if self.redoList:
# Get last item off of redo list
redoGroup = self.popRedoGroup()
# Record undo information
nodePathList = map(lambda x: x[0], redoGroup)
self.pushUndo(nodePathList, fResetRedo = 0)
# Redo xform
for pose in redoGroup:
pose[0].setTransform(pose[1])
# Alert anyone who cares
messenger.send('DIRECT_redo')
# UTILITY FUNCTIONS
def message(self, text):
taskMgr.remove('hideDirectMessage')
taskMgr.remove('hideDirectMessageLater')
self.directMessageReadout.reparentTo(aspect2d)
self.directMessageReadout.setText(text)
self.hideDirectMessageLater()
def hideDirectMessageLater(self):
taskMgr.doMethodLater(3.0, self.hideDirectMessage, 'hideDirectMessage')
def hideDirectMessage(self, state):
self.directMessageReadout.reparentTo(hidden)
return Task.done
def useObjectHandles(self):
self.widget = self.manipulationControl.objectHandles
self.widget.reparentTo(SEditor.group)
def hideSelectedNPReadout(self):
self.selectedNPReadout.reparentTo(hidden)
def hideActiveParentReadout(self):
self.activeParentReadout.reparentTo(hidden)
def toggleWidgetVis(self):
self.widget.toggleWidget()
def setCOAMode(self, mode):
self.coaMode = mode
def isEnabled(self):
return self.fEnabled
def addUnpickable(self, item):
for iRay in self.iRayList:
iRay.addUnpickable(item)
def removeUnpickable(self, item):
for iRay in self.iRayList:
iRay.removeUnpickable(item)
def toggleAutoCamera(self):
self.enableAutoCamera = (self.enableAutoCamera+1)%2
if self.enableAutoCamera==1:
self.accept('DH_LoadingComplete', self.autoCameraMove)
else:
self.ignore('DH_LoadingComplete')
return
def autoCameraMove(self, nodePath):
time = 1
node = DirectNodePath(nodePath)
radius = node.getRadius()
center = node.getCenter()
node.dehighlight()
posB = base.camera.getPos()
hprB = base.camera.getHpr()
posE = Point3((radius*-1.41)+center.getX(), (radius*-1.41)+center.getY(), (radius*1.41)+center.getZ())
hprE = Point3(-45, -38, 0)
print posB, hprB
print posE, hprE
posInterval1 = base.camera.posInterval(time, posE, bakeInStart = 1)
posInterval2 = base.camera.posInterval(time, posB, bakeInStart = 1)
hprInterval1 = base.camera.hprInterval(time, hprE, bakeInStart = 1)
hprInterval2 = base.camera.hprInterval(time, hprB, bakeInStart = 1)
parallel1 = Parallel(posInterval1, hprInterval1)
parallel2 = Parallel(posInterval2, hprInterval2)
Sequence(Wait(7), parallel1, Wait(1), parallel2).start()
return
class DisplayRegionContext(DirectObject):
regionCount = 0
def __init__(self, cam):
self.cam = cam
self.camNode = self.cam.node()
self.camLens = self.camNode.getLens()
# set lens change callback
changeEvent = 'dr%d-change-event' % DisplayRegionContext.regionCount
DisplayRegionContext.regionCount += 1
self.camLens.setChangeEvent(changeEvent)
self.accept(changeEvent, self.camUpdate)
self.iRay = SelectionRay(self.cam)
self.nearVec = Vec3(0)
self.mouseX = 0.0
self.mouseY = 0.0
# A Camera node can have more than one display region
# associated with it. Here I assume that there is only
# one display region per camera, since we are defining a
# display region on a per-camera basis. See note in
# DisplayRegionList.__init__()
try:
self.dr = self.camNode.getDr(0)
except:
self.dr = self.camNode.getDisplayRegion(0)
left = self.dr.getLeft()
right = self.dr.getRight()
bottom = self.dr.getBottom()
top = self.dr.getTop()
self.originX = left+right-1
self.originY = top+bottom-1
self.scaleX = 1.0/(right-left)
self.scaleY = 1.0/(top-bottom)
self.setOrientation()
self.camUpdate()
def __getitem__(self,key):
return self.__dict__[key]
def setOrientation(self):
# MRM This assumes orientation is set on transform above cam
hpr = self.cam.getHpr()
if hpr[2] < 135 and hpr[2]>45 or hpr[2]>225 and hpr[2]<315:
self.isSideways = 1
elif hpr[2] > -135 and hpr[2] < -45 or hpr[2] < -225 and hpr[2] > -315:
self.isSideways = 1
else:
self.isSideways = 0
# The following take into consideration sideways displays
def getHfov(self):
if self.isSideways:
return self.camLens.getVfov()
else:
return self.camLens.getHfov()
def getVfov(self):
if self.isSideways:
return self.camLens.getHfov()
else:
return self.camLens.getVfov()
def setHfov(self,hfov):
if self.isSideways:
self.camLens.setFov(self.camLens.getHfov(), hfov)
else:
self.camLens.setFov(hfov, self.camLens.getVfov())
def setVfov(self,vfov):
if self.isSideways:
self.camLens.setFov(vfov, self.camLens.getVfov())
else:
self.camLens.setFov(self.camLens.getHfov(), vfov)
def setFov(self,hfov,vfov):
if self.isSideways:
self.camLens.setFov(vfov, hfov)
else:
self.camLens.setFov(hfov, vfov)
def getWidth(self):
prop = base.win.getProperties()
if prop.hasSize():
return prop.getXSize()
else:
return 640
def getHeight(self):
prop = base.win.getProperties()
if prop.hasSize():
return prop.getYSize()
else:
return 480
def camUpdate(self, lens = None):
# Window Data
self.near = self.camLens.getNear()
self.far = self.camLens.getFar()
self.fovH = self.camLens.getHfov()
self.fovV = self.camLens.getVfov()
self.nearWidth = math.tan(deg2Rad(self.fovH * 0.5)) * self.near * 2.0
self.nearHeight = math.tan(deg2Rad(self.fovV * 0.5)) * self.near * 2.0
self.left = -self.nearWidth * 0.5
self.right = self.nearWidth * 0.5
self.top = self.nearHeight * 0.5
self.bottom = -self.nearHeight * 0.5
def mouseUpdate(self):
# Mouse Data
# Last frame
self.mouseLastX = self.mouseX
self.mouseLastY = self.mouseY
# Values for this frame
# This ranges from -1 to 1
if (base.mouseWatcherNode.hasMouse()):
self.mouseX = base.mouseWatcherNode.getMouseX()
self.mouseY = base.mouseWatcherNode.getMouseY()
self.mouseX = (self.mouseX-self.originX)*self.scaleX
self.mouseY = (self.mouseY-self.originY)*self.scaleY
# Delta percent of window the mouse moved
self.mouseDeltaX = self.mouseX - self.mouseLastX
self.mouseDeltaY = self.mouseY - self.mouseLastY
self.nearVec.set((self.nearWidth*0.5) * self.mouseX,
self.near,
(self.nearHeight*0.5) * self.mouseY)
class DisplayRegionList(DirectObject):
def __init__(self):
self.displayRegionList = []
i = 0
# Things are funky if we are oobe
if (hasattr(base, 'oobeMode') and base.oobeMode):
# assume we only have one cam at this point
drc = DisplayRegionContext(base.cam)
self.displayRegionList.append(drc)
else:
# MRM: Doesn't properly handle multiple camera groups anymore
# Assumes everything is under main camera
# This is following the old way of setting up
# display regions. A display region is set up for
# each camera node in the scene graph. This was done
# so that only display regions in the scene graph are
# considered. The right way to do this is to set up
# a display region for each real display region, and then
# keep track of which are currently active (e.g. use a flag)
# processing only them.
for camIndex in range(len(base.camList)):
cam = base.camList[camIndex]
if cam.getName()=='<noname>':
cam.setName('Camera%d' % camIndex)
drc = DisplayRegionContext(cam)
self.displayRegionList.append(drc)
self.accept("DIRECT-mouse1",self.mouseUpdate)
self.accept("DIRECT-mouse2",self.mouseUpdate)
self.accept("DIRECT-mouse3",self.mouseUpdate)
self.accept("DIRECT-mouse1Up",self.mouseUpdate)
self.accept("DIRECT-mouse2Up",self.mouseUpdate)
self.accept("DIRECT-mouse3Up",self.mouseUpdate)
def __getitem__(self, index):
return self.displayRegionList[index]
def __len__(self):
return len(self.displayRegionList)
def updateContext(self):
self.contextTask(None)
def setNearFar(self, near, far):
for dr in self.displayRegionList:
dr.camLens.setNearFar(near, far)
def setNear(self, near):
for dr in self.displayRegionList:
dr.camLens.setNear(near)
def setFar(self, far):
for dr in self.displayRegionList:
dr.camLens.setFar(far)
def setFov(self, hfov, vfov):
for dr in self.displayRegionList:
dr.setFov(hfov, vfov)
def setHfov(self, fov):
for dr in self.displayRegionList:
dr.setHfov(fov)
def setVfov(self, fov):
for dr in self.displayRegionList:
dr.setVfov(fov)
def mouseUpdate(self, modifiers = DIRECT_NO_MOD):
for dr in self.displayRegionList:
dr.mouseUpdate()
SEditor.dr = self.getCurrentDr()
def getCurrentDr(self):
for dr in self.displayRegionList:
if (dr.mouseX >= -1.0 and dr.mouseX <= 1.0 and
dr.mouseY >= -1.0 and dr.mouseY <= 1.0):
return dr
return self.displayRegionList[0]
def start(self):
# First shutdown any existing task
self.stop()
# Start a new context task
self.spawnContextTask()
def stop(self):
# Kill the existing context task
taskMgr.remove('DIRECTContextTask')
def spawnContextTask(self):
taskMgr.add(self.contextTask, 'DIRECTContextTask')
def removeContextTask(self):
taskMgr.remove('DIRECTContextTask')
def contextTask(self, state):
# Window Data
self.mouseUpdate()
# hack to test movement
return Task.cont
# Create one
#__builtins__['direct'] = base.direct = DirectSession()

View file

@ -0,0 +1,418 @@
#################################################################
# seTree.py
# Originally from Tree.py
# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#
# This class actually decides the behavior of the sceneGraphExplorer
# You might feel it realy looks like the original one, but we actually did a lots of change in it.
# such as, when selection happend in other place, such as picking directly inside the scene. or,
# when user removed something by hot key.
# The rename process has also been changed. It won't be rename in here anymore.
# Instead, here we will send out a message to sceneEditor to reaname the target.
#
#################################################################
import os, sys, string, Pmw, Tkinter
from direct.showbase.DirectObject import DirectObject
from Tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
from pandac.PandaModules import *
# Initialize icon directory
ICONDIR = getModelPath().findFile(Filename('icons')).toOsSpecific()
if not os.path.isdir(ICONDIR):
raise RuntimeError, "can't find DIRECT icon directory (%s)" % `ICONDIR`
class TreeNode:
def __init__(self, canvas, parent, item, menuList = []):
self.canvas = canvas
self.parent = parent
self.item = item
self.state = 'collapsed'
self.selected = 0
self.children = {}
self.kidKeys = []
self.x = self.y = None
self.iconimages = {} # cache of PhotoImage instances for icons
self.menuList = menuList
self.menuVar = IntVar()
self.menuVar.set(0)
self._popupMenu = None
self.image_id = None
if self.menuList:
if self.menuList[-1] == 'Separator':
self.menuList = self.menuList[:-1]
self._popupMenu = Menu(self.canvas, tearoff = 0)
for i in range(len(self.menuList)):
item = self.menuList[i]
if item == 'Separator':
self._popupMenu.add_separator()
else:
self._popupMenu.add_radiobutton(
label = item,
variable = self.menuVar,
value = i,
indicatoron = 0,
command = self.popupMenuCommand)
def destroy(self):
for key in self.kidKeys:
c = self.children[key]
del self.children[key]
c.destroy()
self.parent = None
def geticonimage(self, name):
try:
return self.iconimages[name]
except KeyError:
pass
file, ext = os.path.splitext(name)
ext = ext or ".gif"
fullname = os.path.join(ICONDIR, file + ext)
image = PhotoImage(master=self.canvas, file=fullname)
self.iconimages[name] = image
return image
def select(self, event=None):
if self.selected:
return
self.deselectall()
self.selected = 1
if self.parent != None:
if self.parent.state == 'expanded':
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
self.item.OnSelect(event)
def deselect(self, event=None):
if not self.selected:
return
self.selected = 0
if self.parent != None:
if self.parent.state == 'expanded':
self.canvas.delete(self.image_id)
self.drawicon()
self.drawtext()
def deselectall(self):
if self.parent:
self.parent.deselectall()
else:
self.deselecttree()
def deselecttree(self):
if self.selected:
self.deselect()
for key in self.kidKeys:
child = self.children[key]
child.deselecttree()
def flip(self, event=None):
if self.state == 'expanded':
self.collapse()
else:
self.expand()
self.item.OnDoubleClick()
return "break"
def popupMenu(self, event=None):
if self._popupMenu:
self._popupMenu.post(event.widget.winfo_pointerx(),
event.widget.winfo_pointery())
return "break"
def popupMenuCommand(self):
command = self.menuList[self.menuVar.get()]
self.item.MenuCommand(command)
if self.parent and (command != 'Update Explorer'):
# Update parent to try to keep explorer up to date
self.parent.update()
def expand(self, event=None):
if not self.item.IsExpandable():
return
if self.state != 'expanded':
self.state = 'expanded'
self.update()
self.view()
def collapse(self, event=None):
if self.state != 'collapsed':
self.state = 'collapsed'
self.update()
def view(self):
top = self.y - 2
bottom = self.lastvisiblechild().y + 17
height = bottom - top
visible_top = self.canvas.canvasy(0)
visible_height = self.canvas.winfo_height()
visible_bottom = self.canvas.canvasy(visible_height)
if visible_top <= top and bottom <= visible_bottom:
return
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
if top >= visible_top and height <= visible_height:
fraction = top + height - visible_height
else:
fraction = top
fraction = float(fraction) / y1
self.canvas.yview_moveto(fraction)
def reveal(self):
# Make sure all parent nodes are marked as expanded
parent = self.parent
while parent:
if parent.state == 'collapsed':
parent.state = 'expanded'
parent = parent.parent
else:
break
# Redraw tree accordingly
self.update()
# Bring this item into view
self.view()
def lastvisiblechild(self):
if self.kidKeys and self.state == 'expanded':
return self.children[self.kidKeys[-1]].lastvisiblechild()
else:
return self
def update(self):
if self.parent:
self.parent.update()
else:
oldcursor = self.canvas['cursor']
self.canvas['cursor'] = "watch"
self.canvas.update()
self.canvas.delete(Tkinter.ALL) # XXX could be more subtle
self.draw(7, 2)
x0, y0, x1, y1 = self.canvas.bbox(Tkinter.ALL)
self.canvas.configure(scrollregion=(0, 0, x1, y1))
self.canvas['cursor'] = oldcursor
def draw(self, x, y):
# XXX This hard-codes too many geometry constants!
self.x, self.y = x, y
self.drawicon()
self.drawtext()
if self.state != 'expanded':
return y+17
# draw children
sublist = self.item._GetSubList()
if not sublist:
# IsExpandable() was mistaken; that's allowed
return y+17
self.kidKeys = []
for item in sublist:
key = item.GetKey()
if self.children.has_key(key):
child = self.children[key]
else:
child = TreeNode(self.canvas, self, item, self.menuList)
self.children[key] = child
self.kidKeys.append(key)
# Remove unused children
for key in self.children.keys():
if key not in self.kidKeys:
del(self.children[key])
cx = x+20
cy = y+17
cylast = 0
for key in self.kidKeys:
child = self.children[key]
cylast = cy
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
cy = child.draw(cx, cy)
if child.item.IsExpandable():
if child.state == 'expanded':
iconname = "minusnode"
callback = child.collapse
else:
iconname = "plusnode"
callback = child.expand
image = self.geticonimage(iconname)
id = self.canvas.create_image(x+9, cylast+7, image=image)
# XXX This leaks bindings until canvas is deleted:
self.canvas.tag_bind(id, "<1>", callback)
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
fill="gray50")
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
return cy
def drawicon(self):
if self.selected:
imagename = (self.item.GetSelectedIconName() or
self.item.GetIconName() or
"openfolder")
else:
imagename = self.item.GetIconName() or "folder"
image = self.geticonimage(imagename)
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
self.image_id = id
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
self.canvas.tag_bind(id, "<3>", self.popupMenu)
def drawtext(self, text=None):
textx = self.x+20-1
texty = self.y-1
labeltext = self.item.GetLabelText()
if labeltext:
id = self.canvas.create_text(textx, texty, anchor="nw",
text=labeltext)
self.canvas.tag_bind(id, "<1>", self.select)
self.canvas.tag_bind(id, "<Double-1>", self.flip)
x0, y0, x1, y1 = self.canvas.bbox(id)
textx = max(x1, 200) + 10
if text==None:
text = self.item.GetText() or "<no text>"
try:
self.entry
except AttributeError:
pass
else:
self.edit_finish()
try:
label = self.label
except AttributeError:
# padding carefully selected (on Windows) to match Entry widget:
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
if self.selected:
self.label.configure(fg="white", bg="darkblue")
else:
fg = self.item.GetTextFg()
self.label.configure(fg=fg, bg="white")
id = self.canvas.create_window(textx, texty,
anchor="nw", window=self.label)
self.label.bind("<1>", self.select_or_edit)
self.label.bind("<Double-1>", self.flip)
self.label.bind("<3>", self.popupMenu)
# Update text if necessary
if text != self.label['text']:
self.label['text'] = text
self.text_id = id
def select_or_edit(self, event=None):
if self.selected and self.item.IsEditable():
text = self.item.GetTextForEdit()
self.label['text'] = text
self.drawtext(text)
self.edit(event)
else:
self.select(event)
def edit(self, event=None):
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
self.entry.insert(0, self.label['text'])
self.entry.selection_range(0, Tkinter.END)
self.entry.pack(ipadx=5)
self.entry.focus_set()
self.entry.bind("<Return>", self.edit_finish)
self.entry.bind("<Escape>", self.edit_cancel)
def edit_finish(self, event=None):
try:
entry = self.entry
del self.entry
except AttributeError:
return
text = entry.get()
entry.destroy()
if text and text != self.item.GetText():
self.item.SetText(text)
text = self.item.GetText()
self.label['text'] = text
self.drawtext()
self.canvas.focus_set()
def edit_cancel(self, event=None):
self.drawtext()
self.canvas.focus_set()
def find(self, searchKey):
# Search for a node who's key matches the given key
# Is it this node
if searchKey == self.item.GetKey():
return self
# Nope, check the children
sublist = self.item._GetSubList()
for item in sublist:
key = item.GetKey()
# Use existing child or create new TreeNode if none exists
if self.children.has_key(key):
child = self.children[key]
else:
child = TreeNode(self.canvas, self, item, self.menuList)
# Update local list of children and keys
self.children[key] = child
self.kidKeys.append(key)
# See if node is child (or one of child's descendants)
retVal = child.find(searchKey)
if retVal:
return retVal
# Not here
return None
class TreeItem:
"""Abstract class representing tree items.
Methods should typically be overridden, otherwise a default action
is used.
"""
def __init__(self):
"""Constructor. Do whatever you need to do."""
def GetText(self):
"""Return text string to display."""
def GetTextFg(self):
return "black"
def GetLabelText(self):
"""Return label text string to display in front of text (if any)."""
def IsExpandable(self):
"""Return whether there are subitems."""
return 1
def _GetSubList(self):
"""Do not override! Called by TreeNode."""
if not self.IsExpandable():
return []
sublist = self.GetSubList()
return sublist
def IsEditable(self):
"""Return whether the item's text may be edited."""
def SetText(self, text):
"""Change the item's text (if it is editable)."""
def GetIconName(self):
"""Return name of icon to be displayed normally."""
def GetSelectedIconName(self):
"""Return name of icon to be displayed when selected."""
def GetSubList(self):
"""Return list of items forming sublist."""
def OnDoubleClick(self):
"""Called on a double-click on the item."""
def OnSelect(self):
"""Called when item selected."""
def GetTextForEdit(self):
"""Called before editting the item."""

9
build/nirai/panda3d/direct/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.pyc
*.pyo
/__init__.py
# These are files that are generated within the source tree by the
# ppremake system.
Makefile
pp.dep
/built/
Opt?-*

View file

@ -0,0 +1,60 @@
//
// Package.pp
//
// This file defines certain configuration variables that are to be
// written into the various make scripts. It is processed by ppremake
// (along with the Sources.pp files in each of the various
// directories) to generate build scripts appropriate to each
// environment.
//
// This is the package-specific file, which should be at the top of
// every source hierarchy. It generally gets the ball rolling, and is
// responsible for explicitly including all of the relevent Config.pp
// files.
// What is the name and version of this source tree?
#if $[eq $[PACKAGE],]
#define PACKAGE direct
#define VERSION 0.80
#endif
// Where should we find the PANDA source directory?
#if $[PANDA_SOURCE]
#define PANDA_SOURCE $[unixfilename $[PANDA_SOURCE]]
#elif $[or $[CTPROJS],$[PANDA]]
// If we are presently attached, use the environment variable.
#define PANDA_SOURCE $[unixfilename $[PANDA]]
#if $[eq $[PANDA],]
#error You seem to be attached to some trees, but not PANDA!
#endif
#else
// Otherwise, if we are not attached, we guess that the source is a
// sibling directory to this source root.
#define PANDA_SOURCE $[standardize $[TOPDIR]/../panda]
#endif
// Where should we install DIRECT?
#if $[DIRECT_INSTALL]
#define DIRECT_INSTALL $[unixfilename $[DIRECT_INSTALL]]
#elif $[CTPROJS]
#set DIRECT $[unixfilename $[DIRECT]]
#define DIRECT_INSTALL $[DIRECT]/built
#if $[eq $[DIRECT],]
#error You seem to be attached to some trees, but not DIRECT!
#endif
#else
#defer DIRECT_INSTALL $[unixfilename $[INSTALL_DIR]]
#endif
// Also get the PANDA Package file and everything that includes.
#if $[not $[isfile $[PANDA_SOURCE]/Package.pp]]
#printvar PANDA_SOURCE
#error PANDA source directory not found from direct! Are you attached properly?
#endif
#include $[PANDA_SOURCE]/Package.pp
// Define the inter-tree dependencies.
#define NEEDS_TREES panda $[NEEDS_TREES]
#define DEPENDABLE_HEADER_DIRS $[DEPENDABLE_HEADER_DIRS] $[PANDA_INSTALL]/include

View file

@ -0,0 +1,11 @@
// This is the toplevel directory for a package.
#define DIR_TYPE toplevel
#define SAMPLE_SOURCE_FILE src/directbase/directbase.cxx
#define REQUIRED_TREES dtool panda
#define EXTRA_DIST \
Sources.pp Config.pp Package.pp
#define PYTHON_PACKAGE 1

View file

@ -0,0 +1,7 @@
// This is a group directory: a directory level above a number of
// source subdirectories.
#define DIR_TYPE group
// The metalibs directory always depends on the src directory.
#define DEPENDS src

View file

@ -0,0 +1,32 @@
// DIR_TYPE "metalib" indicates we are building a shared library that
// consists mostly of references to other shared libraries. Under
// Windows, this directly produces a DLL (as opposed to the regular
// src libraries, which don't produce anything but a pile of OBJ files
// under Windows).
#define DIR_TYPE metalib
#define BUILDING_DLL BUILDING_DIRECT
#define USE_PACKAGES native_net
#define COMPONENT_LIBS \
p3directbase p3dcparser p3showbase p3deadrec p3directd p3interval p3distributed p3motiontrail p3http
#define OTHER_LIBS \
panda:m \
pandaexpress:m \
p3parametrics:c \
p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
p3dtoolutil:c p3dtoolbase:c p3dtool:m \
p3express:c p3pstatclient:c p3prc:c p3pandabase:c p3linmath:c \
p3putil:c p3display:c p3event:c p3pgraph:c p3pgraphnodes:c \
p3gsgbase:c p3gobj:c p3mathutil:c \
p3downloader:c p3pnmimage:c p3chan:c \
p3pipeline:c p3cull:c \
$[if $[HAVE_NET],p3net:c] $[if $[WANT_NATIVE_NET],p3nativenet:c]
#begin metalib_target
#define TARGET p3direct
#define SOURCES direct.cxx
#end metalib_target

View file

@ -0,0 +1,9 @@
// Filename: direct.cxx
// Created by: drose (18May00)
//
////////////////////////////////////////////////////////////////////
// This is a dummy file whose sole purpose is to give the compiler
// something to compile when making libdirect.so in NO_DEFER mode,
// which generates an empty library that itself links with all the
// other shared libraries that make up libdirect.

View file

@ -0,0 +1,4 @@
// This is a group directory: a directory level above a number of
// source subdirectories.
#define DIR_TYPE group

Some files were not shown because too many files have changed in this diff Show more