Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,69 @@
|
||||
The Nenya library
|
||||
-----------------
|
||||
|
||||
The Nenya library provides various facilities for making networked multiplayer
|
||||
games. It's various packages include:
|
||||
|
||||
* geom, util - basic tools for doing data structure manipulation and some
|
||||
geometry math
|
||||
* resource - tools for bundling, deploying and managing media (images,
|
||||
sounds, etc.) with a game
|
||||
* media - a framework for doing "active" rendering in Java
|
||||
* media.image - tools for loading, caching, manipulating and displaying images
|
||||
* media.sound - tools for loading, caching, and playing audio
|
||||
* media.animation, media.sprite - works in concert with the active
|
||||
rendering system and provides tools for defining and manipulating
|
||||
sprites (graphical entities that follow paths) and animations
|
||||
(graphical entities that affect the display in other ways)
|
||||
* miso - a framework for defining and displaying isometrically rendered scenes
|
||||
* cast - a framework for defining and using recolorable, composited
|
||||
characters with different poses and actions
|
||||
|
||||
Documentation is somewhat sparse at the moment, but inspection of the code in
|
||||
the tests/ directory shows examples of use of many features of the library.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building the library is very simple. First ensure that the necessary third
|
||||
party jar files are available in the lib/ directory. See lib/README for a list
|
||||
of the necessary third party jar files and how to get them.
|
||||
|
||||
The library is built using Ant, a modern build tool written in and for Java. If
|
||||
you aren't already using Ant for other projects, it can be found here:
|
||||
|
||||
http://ant.apache.org/
|
||||
|
||||
Invoke ant with any of the following targets:
|
||||
|
||||
all: builds the distribution files and javadoc documentation
|
||||
compile: builds only the class files (dist/classes)
|
||||
javadoc: builds only the javadoc documentation (dist/docs)
|
||||
dist: builds the distribution jar files (dist/*.jar)
|
||||
|
||||
Distribution
|
||||
------------
|
||||
|
||||
The Nenya library is released under the LGPL. The most recent version of the
|
||||
library is available here:
|
||||
|
||||
http://www.threerings.net/code/nenya/
|
||||
|
||||
Contribution
|
||||
------------
|
||||
|
||||
Contributions to Nenya are welcome. Read-only access to the subversion
|
||||
repository is available at:
|
||||
|
||||
svn://code.threerings.net/narya/trunk
|
||||
|
||||
Patches can be mailed to Michael Bayne <mdb@threerings.net>.
|
||||
|
||||
Contact Information
|
||||
-------------------
|
||||
|
||||
Nenya is actively developed by the scurvy dogs at Three Rings Design, Inc.
|
||||
Contact Michael Bayne <mdb@threerings.net> with questions, comments and other
|
||||
wordly endeavors.
|
||||
|
||||
$Id$
|
||||
@@ -0,0 +1,323 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!-- build configuration -->
|
||||
<project name="nenya" default="compile" basedir=".">
|
||||
|
||||
<!-- import overriding properties -->
|
||||
<property file="build.properties"/>
|
||||
|
||||
<!-- configuration parameters -->
|
||||
<property name="app.name" value="nenya"/>
|
||||
<property name="deploy.dir" value="dist"/>
|
||||
<property name="savedoc.dir" value="docs"/>
|
||||
<property name="lib.version" value="1.0"/>
|
||||
|
||||
<!-- derived properties -->
|
||||
<property name="javadoc.home" value="${deploy.dir}/docs"/>
|
||||
<property name="classes.dir" value="${deploy.dir}/classes"/>
|
||||
|
||||
<!-- declare our classpath business -->
|
||||
<path id="classpath">
|
||||
<pathelement location="${classes.dir}"/>
|
||||
<fileset dir="lib" includes="**/*.jar"/>
|
||||
</path>
|
||||
|
||||
<!-- generates additional methods for distributed object classes -->
|
||||
<target name="gendobj" depends="prepare">
|
||||
<taskdef name="dobj" classpathref="classpath"
|
||||
classname="com.threerings.presents.tools.GenDObjectTask"/>
|
||||
<!-- make sure the dobject class files are all compiled -->
|
||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||
debug="on" optimize="${build.optimize}" deprecation="on"
|
||||
source="1.5" target="1.5">
|
||||
<classpath refid="classpath"/>
|
||||
<include name="**/*Object.java"/>
|
||||
</javac>
|
||||
<!-- now generate the associated files -->
|
||||
<dobj classpathref="classpath">
|
||||
<fileset dir="src/java" includes="**/*Object.java"/>
|
||||
</dobj>
|
||||
</target>
|
||||
|
||||
<!-- generates marshaller and dispatcher classes for all invocation -->
|
||||
<!-- service declarations -->
|
||||
<target name="genservice">
|
||||
<taskdef name="service" classpathref="classpath"
|
||||
classname="com.threerings.presents.tools.GenServiceTask"/>
|
||||
<!-- make sure the service class files are all compiled -->
|
||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||
debug="on" optimize="${build.optimize}" deprecation="on"
|
||||
source="1.5" target="1.5">
|
||||
<classpath refid="classpath"/>
|
||||
<include name="**/*Service.java"/>
|
||||
<exclude name="**/InvocationService.java"/>
|
||||
</javac>
|
||||
<!-- now generate the associated files -->
|
||||
<service header="lib/SOURCE_HEADER" classpathref="classpath">
|
||||
<fileset dir="src/java" includes="**/*Service.java"
|
||||
excludes="**/InvocationService.java"/>
|
||||
<providerless service="AdminService"/>
|
||||
<providerless service="ChatService"/>
|
||||
<providerless service="SpeakService"/>
|
||||
<providerless service="LocationService"/>
|
||||
<providerless service="BodyService"/>
|
||||
<providerless service="SimulatorService"/>
|
||||
<providerless service="ParlorService"/>
|
||||
<providerless service="TimeBaseService"/>
|
||||
<providerless service="SpotService"/>
|
||||
<providerless service="ZoneService"/>
|
||||
<providerless service="SceneService"/>
|
||||
</service>
|
||||
</target>
|
||||
|
||||
<!-- generates sender and decoder classes for all invocation -->
|
||||
<!-- receiver declarations -->
|
||||
<target name="genreceiver">
|
||||
<taskdef name="receiver" classpathref="classpath"
|
||||
classname="com.threerings.presents.tools.GenReceiverTask"/>
|
||||
<!-- make sure the receiver class files are all compiled -->
|
||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||
debug="on" optimize="${build.optimize}" deprecation="on"
|
||||
source="1.5" target="1.5">
|
||||
<classpath refid="classpath"/>
|
||||
<include name="**/*Receiver.java"/>
|
||||
<exclude name="**/InvocationReceiver.java"/>
|
||||
</javac>
|
||||
<!-- now generate the associated files -->
|
||||
<receiver header="lib/SOURCE_HEADER" classpathref="classpath">
|
||||
<fileset dir="src/java" includes="**/*Receiver.java"
|
||||
excludes="**/InvocationReceiver.java"/>
|
||||
</receiver>
|
||||
</target>
|
||||
|
||||
<!-- checks the availability of certain libraries -->
|
||||
<target name="check-available">
|
||||
<available property="lwjgl.present"
|
||||
classname="org.lwjgl.LWJGLException" classpathref="classpath"/>
|
||||
<echo message="Have LWJGL: ${lwjgl.present}"/>
|
||||
<available property="jme.present"
|
||||
classname="com.jme.scene.Node" classpathref="classpath"/>
|
||||
<echo message="Have JME: ${jme.present}"/>
|
||||
<available property="jme-bui.present"
|
||||
classname="com.jmex.bui.BWindow" classpathref="classpath"/>
|
||||
<echo message="Have JME-BUI: ${jme-bui.present}"/>
|
||||
<available property="jme-sound.present"
|
||||
classname="com.jmex.sound.openAL.SoundSystem" classpathref="classpath"/>
|
||||
<echo message="Have OPenAL: ${jme-sound.present}"/>
|
||||
<condition property="build.jme">
|
||||
<and>
|
||||
<isset property="jme.present"/>
|
||||
<isset property="jme-bui.present"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="build.openal">
|
||||
<and>
|
||||
<isset property="lwjgl.present"/>
|
||||
<isset property="jme-sound.present"/>
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
|
||||
<!-- prepares the application directories -->
|
||||
<target name="prepare">
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<mkdir dir="${classes.dir}"/>
|
||||
<mkdir dir="${classes.dir}/rsrc"/>
|
||||
<mkdir dir="${javadoc.home}"/>
|
||||
<copy todir="${classes.dir}">
|
||||
<fileset dir="src/java" includes="**/*.properties"/>
|
||||
<fileset dir="src/java" includes="**/*.tmpl"/>
|
||||
</copy>
|
||||
<copy todir="${classes.dir}/rsrc">
|
||||
<fileset dir="rsrc" includes="**/*" excludes="**/bundles/**"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- cleans out the installed application -->
|
||||
<target name="clean">
|
||||
<delete dir="${deploy.dir}"/>
|
||||
<ant dir="tests" target="clean"/>
|
||||
</target>
|
||||
|
||||
<!-- build the java class files -->
|
||||
<target name="compile" depends="check-available,prepare">
|
||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||
debug="on" optimize="{$build.optimize}" deprecation="on"
|
||||
source="1.5" target="1.5">
|
||||
<classpath refid="classpath"/>
|
||||
<exclude name="com/threerings/jme/**" unless="build.jme"/>
|
||||
<exclude name="com/threerings/openal/**" unless="build.openal"/>
|
||||
<exclude name="**/OggPlayer.java"/>
|
||||
<exclude name="**/ModPlayer.java"/>
|
||||
<exclude name="**/MidiPlayer.java"/>
|
||||
<exclude name="**/Mp3Player.java"/>
|
||||
<!--<compilerarg value="-Xlint:unchecked"/>-->
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- build the native libraries -->
|
||||
<target name="ncompile" depends="prepare">
|
||||
<echo message="Doing native compilation on ${os.name}..."/>
|
||||
<exec os="Linux" dir="src/java/com/threerings/util/keybd/Linux"
|
||||
executable="make"><arg line="install"/></exec>
|
||||
<exec dir="src/java/com/threerings/util/unsafe/${os.name}"
|
||||
executable="make"><arg line="install"/></exec>
|
||||
</target>
|
||||
|
||||
<!-- build the javadoc documentation -->
|
||||
<target name="javadoc" depends="prepare">
|
||||
<javadoc sourcepath="src/java" packagenames="com.threerings.*"
|
||||
destdir="${javadoc.home}" stylesheetfile="docs/stylesheet.css"
|
||||
additionalparam="-breakiterator"
|
||||
link="http://www.threerings.net/code/nenya/nenya/docs/api">
|
||||
<classpath refid="classpath"/>
|
||||
<link href="http://java.sun.com/j2se/1.5/docs/api/"/>
|
||||
<!-- ant documentation is not available online, sorry kids -->
|
||||
<link href="file:///usr/share/doc/ant-doc/javadocs"/>
|
||||
<link href="http://samskivert.com/code/samskivert/samskivert/docs/api"/>
|
||||
<link href="http://www.jmonkeyengine.com/doc"/>
|
||||
</javadoc>
|
||||
<copy todir="${javadoc.home}">
|
||||
<fileset dir="src/java" includes="**/*.png"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- builds the javadocs and stuffs them in a directory where they -->
|
||||
<!-- won't be blown away when we do "clean" next time -->
|
||||
<target name="savedoc" depends="javadoc">
|
||||
<delete dir="${savedoc.dir}/api"/>
|
||||
<copy todir="${savedoc.dir}/api">
|
||||
<fileset dir="${javadoc.home}" includes="**/*"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- builds the various tests -->
|
||||
<target name="tests">
|
||||
<ant dir="tests" target="compile"/>
|
||||
</target>
|
||||
|
||||
<!-- a target for rebuilding everything -->
|
||||
<target name="all"
|
||||
depends="clean,prepare,compile,ncompile,javadoc,tests,dist"/>
|
||||
|
||||
<!-- builds our distribution files (war and jar) -->
|
||||
<target name="dist" depends="prepare,compile,ncompile,tests">
|
||||
<!-- build our various jar files -->
|
||||
<jar destfile="${deploy.dir}/${app.name}-rsrc.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/resource/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-media.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/geom/**"/>
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/media/**"/>
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/util/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-miso.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/miso/**"/>
|
||||
<fileset dir="${classes.dir}" includes="rsrc/config/miso/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-cast.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/cast/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-jme.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/jme/**"/>
|
||||
<fileset dir="${classes.dir}" includes="rsrc/media/jme/**"/>
|
||||
<fileset dir="${classes.dir}" includes="rsrc/i18n/jme/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-openal.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/openal/**"/>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<!-- various bits used by the retroweaver tasks -->
|
||||
<taskdef name="weave" classpathref="classpath"
|
||||
classname="com.rc.retroweaver.ant.RetroWeaverTask"/>
|
||||
<property name="inpre" value="${deploy.dir}/${app.name}"/>
|
||||
<property name="outpre" value="${deploy.dir}/retro/${app.name}"/>
|
||||
|
||||
<!-- a helper task for 'retro' -->
|
||||
<target name="vweave">
|
||||
<path id="retrocp">
|
||||
<pathelement location="/usr/local/jdk1.4/jre/lib/rt.jar"/>
|
||||
<fileset dir="lib" includes="**/*.jar"/>
|
||||
<fileset dir="${deploy.dir}/retro" includes="*.jar"/>
|
||||
</path>
|
||||
<weave inputjar="${inpre}-${which}.jar" outputjar="${outpre}-${which}.jar"
|
||||
failonerror="true">
|
||||
<classpath refid="retrocp"/>
|
||||
</weave>
|
||||
</target>
|
||||
|
||||
<!-- converts our 1.5 code to a 1.4 compatible format -->
|
||||
<target name="retro" depends="dist">
|
||||
<mkdir dir="${deploy.dir}/retro"/>
|
||||
|
||||
<!-- we weave everything a first time without verification so that -->
|
||||
<!-- interdependencies will resolve the second time -->
|
||||
<weave inputjar="${inpre}-cast.jar" outputjar="${outpre}-cast.jar"/>
|
||||
<weave inputjar="${inpre}-micasa.jar" outputjar="${outpre}-micasa.jar"/>
|
||||
<weave inputjar="${inpre}-miso.jar" outputjar="${outpre}-miso.jar"/>
|
||||
<weave inputjar="${inpre}-media.jar" outputjar="${outpre}-media.jar"/>
|
||||
<weave inputjar="${inpre}-rsrc.jar" outputjar="${outpre}-rsrc.jar"/>
|
||||
|
||||
<!-- now weave again with the verifier to check for unweavable 1.5isms -->
|
||||
<antcall target="vweave"><param name="which" value="cast"/></antcall>
|
||||
<antcall target="vweave"><param name="which" value="micasa"/></antcall>
|
||||
<antcall target="vweave"><param name="which" value="miso"/></antcall>
|
||||
<antcall target="vweave"><param name="which" value="media"/></antcall>
|
||||
<antcall target="vweave"><param name="which" value="rsrc"/></antcall>
|
||||
</target>
|
||||
|
||||
<!-- generate a class hierarchy diagram -->
|
||||
<target name="hierviz" depends="prepare,compile">
|
||||
<taskdef name="viztool" classname="com.samskivert.viztool.DriverTask"/>
|
||||
<viztool visualizer="com.samskivert.viztool.hierarchy.HierarchyVisualizer"
|
||||
pkgroot="com.threerings.miso" classes="com.threerings.miso.*"
|
||||
output="hierviz.ps">
|
||||
<classpath refid="classpath"/>
|
||||
</viztool>
|
||||
</target>
|
||||
|
||||
<!-- generate a class hierarchy diagram -->
|
||||
<target name="sumgen" depends="prepare,compile">
|
||||
<taskdef name="viztool" classname="com.samskivert.viztool.DriverTask"/>
|
||||
<viztool visualizer="com.samskivert.viztool.summary.SummaryVisualizer"
|
||||
pkgroot="com.threerings.miso"
|
||||
classes="com.threerings.miso.*Scene.*"
|
||||
output="summary.ps">
|
||||
<classpath refid="base.classpath"/>
|
||||
</viztool>
|
||||
</target>
|
||||
|
||||
<!-- creates a tarball and zipfile for source distribution -->
|
||||
<target name="distrib">
|
||||
<echo message="You may want to stop and run 'ant savedoc' first."/>
|
||||
<echo message="Building ${lib.version} tar.gz distribution..."/>
|
||||
<tar destfile="nenya-${lib.version}.tar.gz" compression="gzip">
|
||||
<tarfileset dir=".." mode="0775" dirmode="0775">
|
||||
<include name="nenya/bin/**"/>
|
||||
<include name="nenya/tests/bin/**"/>
|
||||
<exclude name="**/CVS"/>
|
||||
</tarfileset>
|
||||
<tarfileset dir=".." mode="0664" dirmode="0775">
|
||||
<include name="nenya/**"/>
|
||||
<exclude name="**/CVS"/>
|
||||
<exclude name="nenya/bin/**"/>
|
||||
<exclude name="nenya/tests/bin/**"/>
|
||||
<exclude name="nenya/tests/dist/**"/>
|
||||
<exclude name="nenya/dist/**"/>
|
||||
<exclude name="nenya/code/**"/>
|
||||
<exclude name="nenya/nenya-*.*"/>
|
||||
</tarfileset>
|
||||
</tar>
|
||||
<echo message="Building ${lib.version} zip distribution..."/>
|
||||
<zip destfile="nenya-${lib.version}.zip">
|
||||
<fileset dir="..">
|
||||
<include name="nenya/**"/>
|
||||
<exclude name="**/CVS"/>
|
||||
<exclude name="nenya/dist/**"/>
|
||||
<exclude name="nenya/code/**"/>
|
||||
<exclude name="nenya/nenya-*.*"/>
|
||||
</fileset>
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
Media Design Notes -*- mode: outline -*-
|
||||
|
||||
* Tile system
|
||||
** Classes
|
||||
Tile
|
||||
TileSet
|
||||
|
||||
TileManager
|
||||
TileSetRepository
|
||||
|
||||
TileSetIDBroker
|
||||
|
||||
XMLTileSetParser
|
||||
|
||||
TileSetBundle
|
||||
BundledTileSetRepository
|
||||
TileSetBundler
|
||||
|
||||
** Tile management
|
||||
Managing tilesets by id involves creating tileset bundles from XML tileset
|
||||
descriptions.
|
||||
|
||||
Tileset bundles contain both the tileset images and the tileset metadata
|
||||
(in the form of serialized tileset classes?)
|
||||
|
||||
Tileset bundle creation maintains a stable tileset name to tileset id
|
||||
mapping through the use of a TileSetIDBroker which maintains the name to
|
||||
id mapping (in a database or however it likes)
|
||||
|
||||
Sample tileset XML description (for uniform tiles):
|
||||
|
||||
<tileset type="uniform" name="Tileset name">
|
||||
<imagepath>foo/bar/tiles.png</imagepath>
|
||||
<width>64</width>
|
||||
<height>48</height>
|
||||
<tileCount>16</tileCount>
|
||||
</tileset>
|
||||
|
||||
* 01/07/2003
|
||||
** Image management refactor
|
||||
x TileSet:ImageProvider needs to provide colorized images
|
||||
! Probably give tilesets the ImageManager since they have to give the
|
||||
ImageManager reference to the Tile so that it can lock its colorized
|
||||
source image
|
||||
@@ -0,0 +1,14 @@
|
||||
Media Notes -*- mode: outline -*-
|
||||
|
||||
* Old notes
|
||||
- Possibly modify TileSet to load images via image manager (move image
|
||||
provider up to media so that image manager can use tileset's image
|
||||
provider to load the image data) so that they can be cached (and more
|
||||
importantly, flushed from the cache). Presently tilesets are loaded into
|
||||
memory when tileset bundles are loaded and remain loaded forever.
|
||||
|
||||
* 07/19/2002
|
||||
** Virtual media panel
|
||||
- Make sure doLayout() is getting called soon enough to ensure that we
|
||||
don't improperly scroll the wrong thing if we're hijacked while in the
|
||||
middle of scrolling
|
||||
@@ -0,0 +1,139 @@
|
||||
# Miso Notes -*- mode: outline -*-
|
||||
#
|
||||
# $Id: design.txt 2236 2003-02-04 03:33:38Z mdb $
|
||||
|
||||
* Notes
|
||||
Consider making MISO sufficiently sophisticated to allow the cluster stuff
|
||||
to be implemented as an extension. This would included allowing the XML
|
||||
scene stuff to allow extensions, the scene editor to allow extensions and
|
||||
of course extending the MisoScene class itself.
|
||||
|
||||
* Discussion
|
||||
Date: 07/10/01
|
||||
Venue: General discussion in vast sweeping loft area.
|
||||
Participants: mdb, shaper.
|
||||
|
||||
Main game play uses an isometric graphical view to display the current
|
||||
game area. This section of the screen is referred to as the "scene"
|
||||
hereafter.
|
||||
|
||||
Targetting a screen resolution of 800x600. Toolbar and info areas may
|
||||
be on the right side of the screen, below, or both, still TBD. Scene
|
||||
display area will be either 600x600 or 640x480 correspondingly. Will
|
||||
use JDK 1.2 or 1.3 allowing us to make use of Java 2D and the new Java
|
||||
Sound APIs, among other happy things.
|
||||
|
||||
Scenes are non-scrollable. Exits are expected to be placed largely,
|
||||
though not exclusively, around the borders of the scene (esp. at
|
||||
building entrances.) Moving the mouse over an exit causes an arrow to
|
||||
appear flashing to illustrate that clicking on the exit will move the
|
||||
player to a new scene.
|
||||
|
||||
Characters will be displayed in the scene using "fine" coordinates (an
|
||||
additional grid within each tile) to allow them to move smoothly.
|
||||
|
||||
TBD: art details, how many frames of animation, what kinds of things
|
||||
can be carried, paper-doll-ing of character clothing, possibility of
|
||||
color-replacement to allow increased re-use of clothing art.
|
||||
|
||||
* Design
|
||||
** Scene Description
|
||||
- sid: unique scene identifier
|
||||
- name: scene name
|
||||
- version: scene description file format version
|
||||
- tile array: full NxM set of tiles for L0, sparse for L1+
|
||||
- hot spots: the "zones" wherein people congregate in each scene, a set
|
||||
of scene coords
|
||||
- exits: set of scene coords and associated sids that the exits lead to
|
||||
|
||||
** Potential Classes
|
||||
SceneIDBroker: dynamic scene creation and obtaining/tracking scene ids
|
||||
SceneManager: scene retrieval, caching
|
||||
TileManager: tile retrieval, caching
|
||||
Config: tileset/tile info
|
||||
|
||||
** Tile Sets and Tiles
|
||||
General high-level categories of tiles:
|
||||
|
||||
3 * outside (three prospective themes)
|
||||
ship } item (potentially present in all)
|
||||
house
|
||||
|
||||
Tiles will be identified by a combination of a 16-bit tile set id and
|
||||
a 16-bit tile id within that set.
|
||||
|
||||
Estimating:
|
||||
|
||||
400 tiles per scene
|
||||
1000 scenes = (approx) 1.6 MB (highly compressible)
|
||||
|
||||
Tileset config info associated with each tile set includes, for each
|
||||
row of tiles in the tile set, the "row height" (height of the tiles in
|
||||
that row), and the "tile count" (number of tiles in that row).
|
||||
|
||||
Buildings in the scene, as an example, will then be comprised of a
|
||||
contiguous grouping of tiles that are the same width as all other
|
||||
tiles, but of greater height than the ground tiles. The scene editor
|
||||
tool could automate the placement of buildings by allowing pre-defined
|
||||
groupings of tiles and facilitating rotating building tile entities,
|
||||
etc. as needed, but we'll wait on doing fancy things like this
|
||||
until/unless we decide we need it since it's not yet clear that we
|
||||
need particularly fancy scene design tools.
|
||||
|
||||
Note that the scene editing tools should be designed such that they
|
||||
can be used in part within the game itself, since players will be
|
||||
allowed some degree of freedom w.r.t. designing and placing their own
|
||||
homes (and perhaps landscaping?).
|
||||
|
||||
Tilesets are intended to be used as an entity that contains tiles
|
||||
corresponding to whatever logical groupings seem reasonable. E.g.,
|
||||
perhaps one tileset will contain all "ground" tiles; "spanish house"
|
||||
tiles; etc. Split tilesets as necessary (multiple ground tilesets
|
||||
if/when we have enough to warrant it.) Perhaps number the tileset
|
||||
identifiers into meaningful groupings, i.e., all ground tilesets have
|
||||
a tileset identifier in the range of 0 - 999, all building in 2000 -
|
||||
3000, etc.
|
||||
|
||||
Character animation frames may be performed by passing fully qualified
|
||||
tile id and number of frames to animate, frames must be contiguous
|
||||
tiles thereafter. character images will likely be handled as a
|
||||
special case above and beyond our general tile antics.
|
||||
|
||||
Look into using PNG file format for images. PNG supported in JDK 1.3+
|
||||
and probably JAI 1.1 for earlier JDK versions. use PNG alpha-channel
|
||||
rather than separate masks for handling image transparency.
|
||||
|
||||
Look into perhaps using bitmap masks to reduce the drawable area
|
||||
needed for drawing tiles (e.g. exclude corners of iso squares), but
|
||||
only if noteworthy perf gain achieved. Perhaps other similar
|
||||
worthwhile uses.
|
||||
|
||||
** TBD
|
||||
*** Resource mgmt
|
||||
When is scene data and art loaded, how big are the chunks of data
|
||||
downloaded, what's the user experience during download like.
|
||||
|
||||
*** On-screen "map" display of the surrounding scenes
|
||||
The scenes comprising a town, etc. potentially impacts how we allow
|
||||
placement of exits in a scene since allowing folks to place exits to their
|
||||
houses/bldgs willy-nilly may make map generation wacky. perhaps useful
|
||||
for game designer use as well when laying things out and "surveying the
|
||||
landscape."
|
||||
|
||||
* 1/14/2003
|
||||
** Ground zero
|
||||
- Render all ground and fringe tiles (unprepared) into a single offscreen
|
||||
buffer and use that to repaint the ground instead of going from the
|
||||
original tile images
|
||||
|
||||
* 1/28/2003
|
||||
** Scene revamp
|
||||
- Objects without actions are largely inconsequential (in the model, they
|
||||
exist only as fqtid and coordinates)
|
||||
- Objects with actions have extra data:
|
||||
+ the action string
|
||||
+ a location and orientation associated with the object (for building
|
||||
facades this would be the entrance portal location, for "stations"
|
||||
this would be where the user stands when occupying that station)
|
||||
- Scene model is not used by anyone but the scene impl (so that more
|
||||
sophisticated models and impls can come to exist)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,51 @@
|
||||
% sort out scaling and translating
|
||||
36 36 translate
|
||||
/sf 540 640 div def
|
||||
sf sf scale
|
||||
|
||||
% width/height ratio
|
||||
/whr 10 7.5 div def
|
||||
|
||||
% center things vertically
|
||||
0 whr 640 mul 576 sub 2 div translate
|
||||
|
||||
% translate off to the left one tile and clip
|
||||
64 neg 0 translate
|
||||
64 0 640 576 rectclip
|
||||
|
||||
% draw the up moving diagnals
|
||||
/sx 0 def
|
||||
/sy 288 def
|
||||
/ex 384 def
|
||||
/ey 576 def
|
||||
|
||||
1 1 13 {
|
||||
sx sy moveto
|
||||
ex ey lineto
|
||||
stroke
|
||||
/sx sx 32 add def
|
||||
/ex ex 32 add def
|
||||
/sy sy 24 sub def
|
||||
/ey ey 24 sub def
|
||||
} for
|
||||
|
||||
% draw the down moving diagnals
|
||||
/sx 0 def
|
||||
/sy 288 def
|
||||
/ex 384 def
|
||||
/ey 0 def
|
||||
|
||||
1 1 13 {
|
||||
sx sy moveto
|
||||
ex ey lineto
|
||||
stroke
|
||||
/sx sx 32 add def
|
||||
/ex ex 32 add def
|
||||
/sy sy 24 add def
|
||||
/ey ey 24 add def
|
||||
} for
|
||||
|
||||
% box the whole thing in
|
||||
64 0 640 576 rectstroke
|
||||
|
||||
showpage
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
|
||||
<!-- $Id: scenegroup.xml 474 2001-10-17 22:11:35Z shaper $ -->
|
||||
|
||||
<!-- A simple sample scene group description file. -->
|
||||
|
||||
<scenegroup>
|
||||
|
||||
<scene src="rsrc/scenes/townsquare.xml">
|
||||
<name>Town Square</name>
|
||||
<entrance>South</entrance>
|
||||
<portal src="South" destscene="Harbour" dest="North"/>
|
||||
</scene>
|
||||
|
||||
<scene src="rsrc/scenes/harbour.xml">
|
||||
<name>Harbour</name>
|
||||
<entrance>West</entrance>
|
||||
<portal src="North" destscene="Town Square" dest="South"/>
|
||||
</scene>
|
||||
|
||||
</scenegroup>
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Javadoc style sheet */
|
||||
|
||||
/* Page background color */
|
||||
body {
|
||||
background-color: #FFFFFF;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Courier New, monospace
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: Courier New, monospace
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
h1 { font-size: 145% }
|
||||
|
||||
/* Table colors */
|
||||
.TableHeadingColor { background: #CCCCFF } /* Dark mauve */
|
||||
.TableSubHeadingColor { background: #EEEEFF } /* Light mauve */
|
||||
.TableRowColor { background: #FFFFFF } /* White */
|
||||
|
||||
/* Font used in left-hand frame lists */
|
||||
.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif }
|
||||
.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif }
|
||||
.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif }
|
||||
|
||||
/* Navigation bar fonts and colors */
|
||||
.NavBarCell1 { background-color:#EEEEFF;} /* Light mauve */
|
||||
.NavBarCell1Rev { background-color:#00008B;} /* Dark Blue */
|
||||
.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;}
|
||||
.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
|
||||
|
||||
.NavBarCell2 {
|
||||
font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;
|
||||
}
|
||||
.NavBarCell3 {
|
||||
font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
.example {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
border: 1px solid black;
|
||||
background-color: #FFFF99
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
commons-collections.jar
|
||||
commons-digester.jar
|
||||
commons-io.jar
|
||||
jme-awt.jar
|
||||
jme-bui.jar
|
||||
jme-effects.jar
|
||||
jme.jar
|
||||
jme-model.jar
|
||||
jme-sound.jar
|
||||
jme-terrain.jar
|
||||
lwjgl.jar
|
||||
lwjgl_util.jar
|
||||
narya-base.jar
|
||||
narya-distrib.jar
|
||||
retroweaver-all-1.2.2.jar
|
||||
samskivert.jar
|
||||
xml-writer.jar
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
Required external libraries
|
||||
---------------------------
|
||||
|
||||
The following external Java libraries (JAR files) must be placed (or symlinked)
|
||||
here in the lib/ directory, unless specifically noted as optional.
|
||||
|
||||
* The samskivert library:
|
||||
http://samskivert.com/code/samskivert/
|
||||
|
||||
* Apache Commons libraries: collections, digester and io
|
||||
http://jakarta.apache.org/commons/
|
||||
|
||||
* Megginson Technologies' XMLWriter 0.2 library:
|
||||
http://www.megginson.com/Software/xml-writer-0.2.zip
|
||||
|
||||
* Retroweaver libraries for creating 1.4 compatible versions:
|
||||
http://retroweaver.sourceforge.net/
|
||||
|
||||
* (Optional) jMonkeyEngine 3D scene graph toolkit built on LWJGL:
|
||||
http://www.jmonkeyengine.com/
|
||||
|
||||
Once the jar files are in place (regardless of what they are named), the build
|
||||
system will automatically include them in the compile-time classpath.
|
||||
|
||||
--
|
||||
$Id$
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/nenya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: ActionCache.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* A mechanism for caching composited character action animations on disk.
|
||||
*/
|
||||
public interface ActionCache
|
||||
{
|
||||
/**
|
||||
* Fetches from the cache a composited set of images for a particular
|
||||
* character for a particular action.
|
||||
*/
|
||||
public ActionFrames getActionFrames (
|
||||
CharacterDescriptor descrip, String action);
|
||||
|
||||
/**
|
||||
* Requests that the specified set of action frames for the specified
|
||||
* character be cached.
|
||||
*/
|
||||
public void cacheActionFrames (
|
||||
CharacterDescriptor descrip, String action, ActionFrames frames);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id: ActionFrames.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* Encapsulates a set of frames in each of {@link
|
||||
* DirectionCodes#DIRECTION_COUNT} orientations that are used to render a
|
||||
* character sprite.
|
||||
*/
|
||||
public interface ActionFrames
|
||||
{
|
||||
/**
|
||||
* Returns the number of orientations available in this set of action
|
||||
* frames.
|
||||
*/
|
||||
public int getOrientationCount ();
|
||||
|
||||
/**
|
||||
* Returns the multi-frame image that comprises the frames for the
|
||||
* specified orientation.
|
||||
*/
|
||||
public TrimmedMultiFrameImage getFrames (int orient);
|
||||
|
||||
/**
|
||||
* Returns the x offset from the upper left of the image to the
|
||||
* "origin" for this character frame. A sprite with location (x, y)
|
||||
* will be rendered such that its origin is coincident with that
|
||||
* location.
|
||||
*/
|
||||
public int getXOrigin (int orient, int frameIdx);
|
||||
|
||||
/**
|
||||
* Returns the y offset from the upper left of the image to the
|
||||
* "origin" for this character frame. A sprite with location (x, y)
|
||||
* will be rendered such that its origin is coincident with that
|
||||
* location.
|
||||
*/
|
||||
public int getYOrigin (int orient, int frameIdx);
|
||||
|
||||
/**
|
||||
* Creates a clone of these action frames which will have the supplied
|
||||
* colorizations applied to the frame images.
|
||||
*/
|
||||
public ActionFrames cloneColorized (Colorization[] zations);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// $Id: ActionSequence.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The action sequence class describes a particular character animation
|
||||
* sequence. An animation sequence consists of one or more frames of
|
||||
* animation, renders at a particular frame rate, and has an origin point
|
||||
* that specifies the location of the base of the character in relation to
|
||||
* the bounds of the animation images.
|
||||
*/
|
||||
public class ActionSequence implements Serializable
|
||||
{
|
||||
/**
|
||||
* Defines the name of the default action sequence. When component
|
||||
* tilesets are loaded to build a set of composited images for a
|
||||
* particular action sequence, a check is first made for a component
|
||||
* tileset specific to the action sequence and then for the
|
||||
* component's default tileset if the action specific tileset did not
|
||||
* exist.
|
||||
*/
|
||||
public static final String DEFAULT_SEQUENCE = "default";
|
||||
|
||||
/** The action sequence name. */
|
||||
public String name;
|
||||
|
||||
/** The number of frames per second to show when animating. */
|
||||
public float framesPerSecond;
|
||||
|
||||
/** The position of the character's base for this sequence. */
|
||||
public Point origin = new Point();
|
||||
|
||||
/** Orientation codes for the orientations available for this
|
||||
* action. */
|
||||
public int[] orients;
|
||||
|
||||
/**
|
||||
* Returns a string representation of this action sequence.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[name=" + name + ", framesPerSecond=" + framesPerSecond +
|
||||
", origin=" + origin +
|
||||
", orients=" + (orients == null ? 0 : orients.length) + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: CastPrefs.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
/**
|
||||
* Provides access to runtime configuration parameters for this package
|
||||
* and its subpackages.
|
||||
*/
|
||||
public class CastPrefs
|
||||
{
|
||||
/** Used to load our preferences from a properties file and map them
|
||||
* to the persistent Java preferences repository. */
|
||||
public static Config config = new Config("rsrc/config/cast");
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// $Id: CharacterComponent.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
|
||||
/**
|
||||
* The character component represents a single component that can be
|
||||
* composited with other character components to generate an image
|
||||
* representing a complete character displayable in any of the eight
|
||||
* compass directions as detailed in the {@link Sprite} class direction
|
||||
* constants.
|
||||
*/
|
||||
public class CharacterComponent implements Serializable
|
||||
{
|
||||
/** The unique component identifier. */
|
||||
public int componentId;
|
||||
|
||||
/** The component's name. */
|
||||
public String name;
|
||||
|
||||
/** The class of components to which this one belongs. */
|
||||
public ComponentClass componentClass;
|
||||
|
||||
/**
|
||||
* Constructs a character component with the specified id of the
|
||||
* specified class.
|
||||
*/
|
||||
public CharacterComponent (int componentId, String name,
|
||||
ComponentClass compClass, FrameProvider fprov)
|
||||
{
|
||||
this.componentId = componentId;
|
||||
this.name = name;
|
||||
this.componentClass = compClass;
|
||||
_frameProvider = fprov;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image frames for the specified action animation or null if
|
||||
* no animation for the specified action is available for this component.
|
||||
*
|
||||
* @param type null for the normal action frames or one of the custom
|
||||
* action sub-types: {@link StandardActions#SHADOW_TYPE}, etc.
|
||||
*/
|
||||
public ActionFrames getFrames (String action, String type)
|
||||
{
|
||||
return _frameProvider.getFrames(this, action, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component is equal to the other component. The
|
||||
* comparison is made on <code>componentId</code>.
|
||||
*/
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other instanceof CharacterComponent) {
|
||||
return componentId == ((CharacterComponent)other).componentId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this character component.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
|
||||
/** The entity from which we obtain our animation frames. */
|
||||
protected FrameProvider _frameProvider;
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// $Id: CharacterDescriptor.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.media.image.Colorization;
|
||||
|
||||
/**
|
||||
* The character descriptor object details the components that are
|
||||
* pieced together to create a single character image.
|
||||
*/
|
||||
public class CharacterDescriptor
|
||||
{
|
||||
/**
|
||||
* Constructs a character descriptor.
|
||||
*
|
||||
* @param components the component ids of the individual components
|
||||
* that make up this character.
|
||||
* @param zations the colorizations to apply to each of the character
|
||||
* component images when compositing actions (an array of
|
||||
* colorizations for each component, elements of which can be null to
|
||||
* make it easier to support per-component specialized colorizations).
|
||||
* This can be null if image recolorization is not desired.
|
||||
*/
|
||||
public CharacterDescriptor (int[] components, Colorization[][] zations)
|
||||
{
|
||||
_components = components;
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the component identifiers comprising the
|
||||
* character described by this descriptor.
|
||||
*/
|
||||
public int[] getComponentIds ()
|
||||
{
|
||||
return _components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of colorization arrays to be applied to the
|
||||
* components when compositing action images (one array per
|
||||
* component).
|
||||
*/
|
||||
public Colorization[][] getColorizations ()
|
||||
{
|
||||
return _zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the colorizations to be used by this character descriptor.
|
||||
*/
|
||||
public void setColorizations (Colorization[][] zations)
|
||||
{
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a sensible hashcode for this object.
|
||||
*/
|
||||
public int hashCode ()
|
||||
{
|
||||
int code = 0, clength = _components.length;
|
||||
for (int i = 0; i < clength; i++) {
|
||||
code ^= _components[i];
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this character descriptor to another.
|
||||
*/
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (!(other instanceof CharacterDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// both the component ids and the colorizations must be equal
|
||||
CharacterDescriptor odesc = (CharacterDescriptor)other;
|
||||
if (!Arrays.equals(_components, odesc._components)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Colorization[][] zations = odesc._zations;
|
||||
if (zations == null && _zations == null) {
|
||||
// if neither has colorizations, we're clear
|
||||
return true;
|
||||
|
||||
} else if (zations == null || _zations == null) {
|
||||
// if one has colorizations whilst the other doesn't, they
|
||||
// can't be equal
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, all of the colorizations must be equal as well
|
||||
int zlength = zations.length;
|
||||
if (zlength != _zations.length) {
|
||||
return false;
|
||||
}
|
||||
for (int ii = 0; ii < zlength; ii++) {
|
||||
if (!Arrays.equals(_zations[ii], zations[ii])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this character descriptor.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[cids=" + StringUtil.toString(_components) +
|
||||
", colors=" + StringUtil.toString(_zations) + "]";
|
||||
}
|
||||
|
||||
/** The component identifiers comprising the character. */
|
||||
protected int[] _components;
|
||||
|
||||
/** The colorizations to apply when compositing this character. */
|
||||
protected Colorization[][] _zations;
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
//
|
||||
// $Id: CharacterManager.java 4022 2006-04-14 21:49:11Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.swing.RuntimeAdjust;
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.Throttle;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
import com.threerings.cast.Log;
|
||||
|
||||
/**
|
||||
* The character manager provides facilities for constructing sprites that
|
||||
* are used to represent characters in a scene. It also handles the
|
||||
* compositing and caching of composited character animations.
|
||||
*/
|
||||
public class CharacterManager
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Constructs the character manager.
|
||||
*/
|
||||
public CharacterManager (ImageManager imgr, ComponentRepository crepo)
|
||||
{
|
||||
// keep these around
|
||||
_imgr = imgr;
|
||||
_crepo = crepo;
|
||||
|
||||
// populate our actions table
|
||||
Iterator iter = crepo.enumerateActionSequences();
|
||||
while (iter.hasNext()) {
|
||||
ActionSequence action = (ActionSequence)iter.next();
|
||||
_actions.put(action.name, action);
|
||||
}
|
||||
|
||||
// create a cache for our composited action frames
|
||||
int acsize = _cacheSize.getValue();
|
||||
Log.debug("Creating action cache [size=" + acsize + "k].");
|
||||
_frameCache = new LRUHashMap(acsize*1024, new LRUHashMap.ItemSizer() {
|
||||
public int computeSize (Object value) {
|
||||
return (int)((CompositedMultiFrameImage)
|
||||
value).getEstimatedMemoryUsage();
|
||||
}
|
||||
});
|
||||
_frameCache.setTracking(true); // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component repository being used by this manager.
|
||||
*/
|
||||
public ComponentRepository getComponentRepository ()
|
||||
{
|
||||
return _crepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the character manager to construct instances of this
|
||||
* derived class of {@link CharacterSprite} when creating new sprites.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the supplied class
|
||||
* does not derive from {@link CharacterSprite}.
|
||||
*/
|
||||
public void setCharacterClass (Class charClass)
|
||||
{
|
||||
// sanity check
|
||||
if (!CharacterSprite.class.isAssignableFrom(charClass)) {
|
||||
String errmsg = "Requested to use character sprite class that " +
|
||||
"does not derive from CharacterSprite " +
|
||||
"[class=" + charClass.getName() + "].";
|
||||
throw new IllegalArgumentException(errmsg);
|
||||
}
|
||||
|
||||
// make a note of it
|
||||
_charClass = charClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the character manager to use the provided cache for
|
||||
* composited action animations.
|
||||
*/
|
||||
public void setActionCache (ActionCache cache)
|
||||
{
|
||||
_acache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link CharacterSprite} representing the character
|
||||
* described by the given {@link CharacterDescriptor}, or
|
||||
* <code>null</code> if an error occurs.
|
||||
*
|
||||
* @param desc the character descriptor.
|
||||
*/
|
||||
public CharacterSprite getCharacter (CharacterDescriptor desc)
|
||||
{
|
||||
return getCharacter(desc, _charClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link CharacterSprite} representing the character
|
||||
* described by the given {@link CharacterDescriptor}, or
|
||||
* <code>null</code> if an error occurs.
|
||||
*
|
||||
* @param desc the character descriptor.
|
||||
* @param charClass the {@link CharacterSprite} derived class that
|
||||
* should be instantiated instead of the configured default (which is
|
||||
* set via {@link #setCharacterClass}).
|
||||
*/
|
||||
public CharacterSprite getCharacter (CharacterDescriptor desc,
|
||||
Class charClass)
|
||||
{
|
||||
try {
|
||||
CharacterSprite sprite = (CharacterSprite)
|
||||
charClass.newInstance();
|
||||
sprite.init(desc, this);
|
||||
return sprite;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed to instantiate character sprite " +
|
||||
"[e=" + e + "].");
|
||||
Log.logStackTrace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the composited animation frames for the specified action for a
|
||||
* character with the specified descriptor. The resulting composited
|
||||
* animation will be cached.
|
||||
*
|
||||
* @exception NoSuchComponentException thrown if any of the components in
|
||||
* the supplied descriptor do not exist.
|
||||
* @exception IllegalArgumentException thrown if any of the components
|
||||
* referenced in the descriptor do not support the specified action.
|
||||
*/
|
||||
public ActionFrames getActionFrames (
|
||||
CharacterDescriptor descrip, String action)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
Tuple key = new Tuple(descrip, action);
|
||||
ActionFrames frames = (ActionFrames)_actionFrames.get(key);
|
||||
if (frames == null) {
|
||||
// this doesn't actually composite the images, but prepares an
|
||||
// object to be able to do so
|
||||
frames = createCompositeFrames(descrip, action);
|
||||
_actionFrames.put(key, frames);
|
||||
}
|
||||
|
||||
// periodically report our frame image cache performance
|
||||
if (!_cacheStatThrottle.throttleOp()) {
|
||||
long size = getEstimatedCacheMemoryUsage();
|
||||
int[] eff = _frameCache.getTrackedEffectiveness();
|
||||
Log.debug("CharacterManager LRU [mem=" + (size / 1024) + "k" +
|
||||
", size=" + _frameCache.size() + ", hits=" + eff[0] +
|
||||
", misses=" + eff[1] + "].");
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the character manager that the action sequence for the
|
||||
* given character descriptor is likely to be needed in the near
|
||||
* future and so any efforts that can be made to load it into the
|
||||
* action sequence cache in advance should be undertaken.
|
||||
*
|
||||
* <p> This will eventually be revamped to spiffily load action
|
||||
* sequences in the background.
|
||||
*/
|
||||
public void resolveActionSequence (CharacterDescriptor desc, String action)
|
||||
{
|
||||
try {
|
||||
if (getActionFrames(desc, action) == null) {
|
||||
Log.warning("Failed to resolve action sequence " +
|
||||
"[desc=" + desc + ", action=" + action + "].");
|
||||
}
|
||||
|
||||
} catch (NoSuchComponentException nsce) {
|
||||
Log.warning("Failed to resolve action sequence " +
|
||||
"[nsce=" + nsce + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action sequence instance with the specified name or
|
||||
* null if no such sequence exists.
|
||||
*/
|
||||
public ActionSequence getActionSequence (String action)
|
||||
{
|
||||
return (ActionSequence)_actions.get(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated memory usage in bytes for all images
|
||||
* currently cached by the cached action frames.
|
||||
*/
|
||||
protected long getEstimatedCacheMemoryUsage ()
|
||||
{
|
||||
long size = 0;
|
||||
Iterator iter = _frameCache.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
size += ((CompositedMultiFrameImage)
|
||||
iter.next()).getEstimatedMemoryUsage();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the composited animation frames for the specified action
|
||||
* for a character with the specified descriptor.
|
||||
*
|
||||
* @exception NoSuchComponentException thrown if any of the components
|
||||
* in the supplied descriptor do not exist.
|
||||
* @exception IllegalArgumentException thrown if any of the components
|
||||
* referenced in the descriptor do not support the specified action.
|
||||
*/
|
||||
protected ActionFrames createCompositeFrames (
|
||||
CharacterDescriptor descrip, String action)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
int[] cids = descrip.getComponentIds();
|
||||
int ccount = cids.length;
|
||||
Colorization[][] zations = descrip.getColorizations();
|
||||
|
||||
Log.debug("Compositing action [action=" + action +
|
||||
", descrip=" + descrip + "].");
|
||||
|
||||
// this will be used to construct any shadow layers
|
||||
HashMap shadows = null;
|
||||
|
||||
// maps components by class name for masks
|
||||
HashMap ccomps = new HashMap();
|
||||
|
||||
// create colorized versions of all of the source action frames
|
||||
ArrayList sources = new ArrayList(ccount);
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
sources.add(cframes);
|
||||
CharacterComponent ccomp =
|
||||
(cframes.ccomp = _crepo.getComponent(cids[ii]));
|
||||
ccomps.put(ccomp.componentClass.name, ccomp);
|
||||
|
||||
// load up the main component images
|
||||
ActionFrames source = ccomp.getFrames(action, null);
|
||||
if (source == null) {
|
||||
String errmsg = "Cannot composite action frames; no such " +
|
||||
"action for component [action=" + action +
|
||||
", desc=" + descrip + ", comp=" + ccomp + "]";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
cframes.frames = (zations == null || zations[ii] == null) ?
|
||||
source : source.cloneColorized(zations[ii]);
|
||||
|
||||
// if this component has a shadow, make a note of it
|
||||
if (ccomp.componentClass.isShadowed()) {
|
||||
if (shadows == null) {
|
||||
shadows = new HashMap();
|
||||
}
|
||||
ArrayList shadlist = (ArrayList)
|
||||
shadows.get(ccomp.componentClass.shadow);
|
||||
if (shadlist == null) {
|
||||
shadows.put(ccomp.componentClass.shadow,
|
||||
shadlist = new ArrayList());
|
||||
}
|
||||
shadlist.add(ccomp);
|
||||
}
|
||||
}
|
||||
|
||||
// add any necessary masks
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
ComponentFrames cframes = (ComponentFrames)sources.get(ii);
|
||||
CharacterComponent mcomp = (CharacterComponent)ccomps.get(
|
||||
cframes.ccomp.componentClass.mask);
|
||||
if (mcomp != null) {
|
||||
cframes.frames = compositeMask(action, cframes.ccomp,
|
||||
cframes.frames, mcomp);
|
||||
}
|
||||
}
|
||||
|
||||
// now create any necessary shadow layers
|
||||
if (shadows != null) {
|
||||
Iterator iter = shadows.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry)iter.next();
|
||||
String sclass = (String)entry.getKey();
|
||||
ArrayList scomps = (ArrayList)entry.getValue();
|
||||
ComponentFrames scf = compositeShadow(action, sclass, scomps);
|
||||
if (scf != null) {
|
||||
sources.add(scf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use those to create an entity that will lazily composite things
|
||||
// together as they are needed
|
||||
ComponentFrames[] cfvec = (ComponentFrames[])sources.toArray(
|
||||
new ComponentFrames[sources.size()]);
|
||||
return new CompositedActionFrames(_imgr, _frameCache, action, cfvec);
|
||||
}
|
||||
|
||||
protected ActionFrames compositeMask (
|
||||
String action, CharacterComponent ccomp, ActionFrames cframes,
|
||||
CharacterComponent mcomp)
|
||||
{
|
||||
ActionFrames mframes = mcomp.getFrames(action,
|
||||
StandardActions.CROP_TYPE);
|
||||
if (mframes == null) {
|
||||
return cframes;
|
||||
}
|
||||
return new CompositedActionFrames(
|
||||
_imgr, _frameCache, action, new ComponentFrames[] {
|
||||
new ComponentFrames(ccomp, cframes),
|
||||
new ComponentFrames(mcomp, mframes) }) {
|
||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
||||
return new CompositedMaskedImage(
|
||||
_imgr, _sources, _action, orient);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ComponentFrames compositeShadow (
|
||||
String action, String sclass, ArrayList scomps)
|
||||
{
|
||||
final ComponentClass cclass = _crepo.getComponentClass(sclass);
|
||||
if (cclass == null) {
|
||||
Log.warning("Components reference non-existent shadow layer " +
|
||||
"class [sclass=" + sclass +
|
||||
", scomps=" + StringUtil.toString(scomps) + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
// create a fake component for the shadow layer
|
||||
cframes.ccomp = new CharacterComponent(-1, "shadow", cclass, null);
|
||||
|
||||
ArrayList sources = new ArrayList();
|
||||
for (int ii = 0, ll = scomps.size(); ii < ll; ii++) {
|
||||
ComponentFrames source = new ComponentFrames();
|
||||
source.ccomp = (CharacterComponent)scomps.get(ii);
|
||||
source.frames = source.ccomp.getFrames(
|
||||
action, StandardActions.SHADOW_TYPE);
|
||||
if (source.frames == null) {
|
||||
// skip this shadow component
|
||||
continue;
|
||||
}
|
||||
sources.add(source);
|
||||
}
|
||||
|
||||
// if we ended up with no shadow, no problem!
|
||||
if (sources.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// create custom action frames that use a special compositing
|
||||
// multi-frame image that does the necessary shadow magic
|
||||
ComponentFrames[] svec = (ComponentFrames[])
|
||||
sources.toArray(new ComponentFrames[sources.size()]);
|
||||
cframes.frames = new CompositedActionFrames(
|
||||
_imgr, _frameCache, action, svec) {
|
||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
||||
return new CompositedShadowImage(
|
||||
_imgr, _sources, _action, orient, cclass.shadowAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
return cframes;
|
||||
}
|
||||
|
||||
/** The image manager with whom we interact. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** The component repository. */
|
||||
protected ComponentRepository _crepo;
|
||||
|
||||
/** A table of our action sequences. */
|
||||
protected HashMap _actions = new HashMap();
|
||||
|
||||
/** A table of composited action sequences (these don't reference the
|
||||
* actual image data directly and thus take up little memory). */
|
||||
protected HashMap _actionFrames = new HashMap();
|
||||
|
||||
/** A cache of composited animation frames. */
|
||||
protected LRUHashMap _frameCache;
|
||||
|
||||
/** The character class to be created. */
|
||||
protected Class _charClass = CharacterSprite.class;
|
||||
|
||||
/** The action animation cache, if we have one. */
|
||||
protected ActionCache _acache;
|
||||
|
||||
/** Throttle our cache status logging to once every 30 seconds. */
|
||||
protected Throttle _cacheStatThrottle = new Throttle(1, 30000L);
|
||||
|
||||
/** Register our image cache size with the runtime adjustments
|
||||
* framework. */
|
||||
protected static RuntimeAdjust.IntAdjust _cacheSize =
|
||||
new RuntimeAdjust.IntAdjust(
|
||||
"Size (in kb of memory used) of the character manager LRU " +
|
||||
"action cache [requires restart]", "narya.cast.action_cache_size",
|
||||
CastPrefs.config, 1024);
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
//
|
||||
// $Id: CharacterSprite.java 3785 2005-12-15 22:14:37Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.threerings.media.sprite.ImageSprite;
|
||||
|
||||
/**
|
||||
* A character sprite is a sprite that animates itself while walking
|
||||
* about in a scene.
|
||||
*/
|
||||
public class CharacterSprite extends ImageSprite
|
||||
implements StandardActions
|
||||
{
|
||||
/**
|
||||
* Initializes this character sprite with the specified character
|
||||
* descriptor and character manager. It will obtain animation data
|
||||
* from the supplied character manager.
|
||||
*/
|
||||
public void init (CharacterDescriptor descrip, CharacterManager charmgr)
|
||||
{
|
||||
// keep track of this stuff
|
||||
_descrip = descrip;
|
||||
_charmgr = charmgr;
|
||||
|
||||
// sanity check our values
|
||||
sanityCheckDescrip();
|
||||
|
||||
// assign an arbitrary starting orientation
|
||||
_orient = SOUTHWEST;
|
||||
|
||||
// pass the buck to derived classes
|
||||
didInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after this sprite has been initialized with its character
|
||||
* descriptor and character manager. Derived classes can do post-init
|
||||
* business here.
|
||||
*/
|
||||
protected void didInit ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures this sprite to use the specified character descriptor.
|
||||
*/
|
||||
public void setCharacterDescriptor (CharacterDescriptor descrip)
|
||||
{
|
||||
// keep the new descriptor
|
||||
_descrip = descrip;
|
||||
|
||||
// sanity check our values
|
||||
sanityCheckDescrip();
|
||||
|
||||
// update our action frames
|
||||
updateActionFrames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the action to use when the sprite is at rest. The default
|
||||
* is <code>STANDING</code>.
|
||||
*/
|
||||
public void setRestingAction (String action)
|
||||
{
|
||||
_restingAction = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action to be used when the sprite is at rest. Derived
|
||||
* classes may wish to override this method and vary the action based
|
||||
* on external parameters (or randomly).
|
||||
*/
|
||||
public String getRestingAction ()
|
||||
{
|
||||
return _restingAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the action to use when the sprite is following a path.
|
||||
* The default is <code>WALKING</code>.
|
||||
*/
|
||||
public void setFollowingPathAction (String action)
|
||||
{
|
||||
_followingPathAction = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action to be used when the sprite is following a path.
|
||||
* Derived classes may wish to override this method and vary the
|
||||
* action based on external parameters (or randomly).
|
||||
*/
|
||||
public String getFollowingPathAction ()
|
||||
{
|
||||
return _followingPathAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action sequence used when rendering the character, from
|
||||
* the set of available sequences.
|
||||
*/
|
||||
public void setActionSequence (String action)
|
||||
{
|
||||
// sanity check
|
||||
if (action == null) {
|
||||
Log.warning("Refusing to set null action sequence " + this + ".");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to noop
|
||||
if (action.equals(_action)) {
|
||||
return;
|
||||
}
|
||||
_action = action;
|
||||
updateActionFrames();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setOrientation (int orient)
|
||||
{
|
||||
if (orient < 0 || orient >= FINE_DIRECTION_COUNT) {
|
||||
Log.info("Refusing to set invalid orientation [sprite=" + this +
|
||||
", orient=" + orient + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
int oorient = _orient;
|
||||
super.setOrientation(orient);
|
||||
if (_orient != oorient) {
|
||||
_frames = null;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean hitTest (int x, int y)
|
||||
{
|
||||
// the irect adjustments are to account for our decorations
|
||||
return (_frames != null && _ibounds.contains(x, y) &&
|
||||
_frames.hitTest(_frameIdx, x - _ibounds.x, y - _ibounds.y));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void tick (long tickStamp)
|
||||
{
|
||||
// composite our action frames if something since the last call to
|
||||
// tick caused them to become invalid
|
||||
compositeActionFrames();
|
||||
super.tick(tickStamp);
|
||||
// composite our action frames if something during tick() caused
|
||||
// them to become invalid
|
||||
compositeActionFrames();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void cancelMove ()
|
||||
{
|
||||
super.cancelMove();
|
||||
halt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void pathBeginning ()
|
||||
{
|
||||
super.pathBeginning();
|
||||
|
||||
// enable walking animation
|
||||
setAnimationMode(TIME_BASED);
|
||||
setActionSequence(getFollowingPathAction());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void pathCompleted (long timestamp)
|
||||
{
|
||||
super.pathCompleted(timestamp);
|
||||
halt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
if (_frames != null) {
|
||||
decorateBehind(gfx);
|
||||
// paint the image using _ibounds rather than _bounds which
|
||||
// has been modified to include the bounds of our decorations
|
||||
_frames.paintFrame(gfx, _frameIdx, _ibounds.x, _ibounds.y);
|
||||
decorateInFront(gfx);
|
||||
|
||||
} else {
|
||||
super.paint(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to paint any decorations that should appear behind the
|
||||
* character sprite image.
|
||||
*/
|
||||
protected void decorateBehind (Graphics2D gfx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to paint any decorations that should appear in front of the
|
||||
* character sprite image.
|
||||
*/
|
||||
protected void decorateInFront (Graphics2D gfx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds our action frames given our current character descriptor
|
||||
* and action sequence. This is called when either of those two things
|
||||
* changes.
|
||||
*/
|
||||
protected void updateActionFrames ()
|
||||
{
|
||||
// get a reference to the action sequence so that we can obtain
|
||||
// our animation frames and configure our frames per second
|
||||
ActionSequence actseq = _charmgr.getActionSequence(_action);
|
||||
if (actseq == null) {
|
||||
String errmsg = "No such action '" + _action + "'.";
|
||||
throw new IllegalArgumentException(errmsg);
|
||||
}
|
||||
|
||||
try {
|
||||
// obtain our animation frames for this action sequence
|
||||
_aframes = _charmgr.getActionFrames(_descrip, _action);
|
||||
|
||||
// clear out our frames so that we recomposite on next tick
|
||||
_frames = null;
|
||||
|
||||
// update the sprite render attributes
|
||||
setFrameRate(actseq.framesPerSecond);
|
||||
|
||||
} catch (NoSuchComponentException nsce) {
|
||||
Log.warning("Character sprite references non-existent " +
|
||||
"component [sprite=" + this + ", err=" + nsce + "].");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed to obtain action frames [sprite=" + this +
|
||||
", descrip=" + _descrip + ", action=" + _action + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called to recomposite our action frames if needed. */
|
||||
protected final void compositeActionFrames ()
|
||||
{
|
||||
if (_frames == null && _aframes != null) {
|
||||
setFrames(_aframes.getFrames(_orient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it easier to track down problems with bogus character descriptors.
|
||||
*/
|
||||
protected void sanityCheckDescrip ()
|
||||
{
|
||||
if (_descrip.getComponentIds() == null ||
|
||||
_descrip.getComponentIds().length == 0) {
|
||||
Log.warning("Invalid character descriptor [sprite=" + this +
|
||||
", descrip=" + _descrip + "].");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected boolean tickPath (long tickStamp)
|
||||
{
|
||||
boolean moved = super.tickPath(tickStamp);
|
||||
// composite our action frames if our path caused them to become
|
||||
// invalid
|
||||
compositeActionFrames();
|
||||
return moved;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void updateRenderOrigin ()
|
||||
{
|
||||
super.updateRenderOrigin();
|
||||
|
||||
// adjust our image bounds to reflect the new location
|
||||
_ibounds.x = _bounds.x + _ioff.x;
|
||||
_ibounds.y = _bounds.y + _ioff.y;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void accomodateFrame (int frameIdx, int width, int height)
|
||||
{
|
||||
// this will update our width and height
|
||||
super.accomodateFrame(frameIdx, width, height);
|
||||
|
||||
// we now need to update the render offset for this frame
|
||||
if (_aframes == null) {
|
||||
Log.warning("Have no action frames! " + _aframes + ".");
|
||||
} else {
|
||||
_oxoff = _aframes.getXOrigin(_orient, frameIdx);
|
||||
_oyoff = _aframes.getYOrigin(_orient, frameIdx);
|
||||
}
|
||||
|
||||
// and cause those changes to be reflected in our bounds
|
||||
updateRenderOrigin();
|
||||
|
||||
// start out with our bounds the same as our image bounds
|
||||
_ibounds.setBounds(_bounds);
|
||||
|
||||
// now we can call down and incorporate the dimensions of any
|
||||
// decorations that will be rendered along with our image
|
||||
unionDecorationBounds(_bounds);
|
||||
|
||||
// compute our new render origin
|
||||
_oxoff = _ox - _bounds.x;
|
||||
_oyoff = _oy - _bounds.y;
|
||||
|
||||
// track the offset from our expanded bounds to our image bounds
|
||||
_ioff.x = _ibounds.x - _bounds.x;
|
||||
_ioff.y = _ibounds.y - _bounds.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #accomodateFrame} to give derived classes an
|
||||
* opportunity to incorporate the bounds of any decorations that will
|
||||
* be drawn along with this sprite. The {@link #_ibounds} rectangle
|
||||
* will contain the bounds of the image that comprises the undecorated
|
||||
* sprite. From that the position and size of decorations can be
|
||||
* computed and unioned with the supplied bounds rectangle (most
|
||||
* likely by using {@link SwingUtilities#computeUnion} which applies
|
||||
* the union in place rather than creating a new rectangle).
|
||||
*/
|
||||
protected void unionDecorationBounds (Rectangle bounds)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sprite animation frame to reflect the cessation of
|
||||
* movement and disables any further animation.
|
||||
*/
|
||||
protected void halt ()
|
||||
{
|
||||
// only do something if we're actually animating
|
||||
if (_animMode != NO_ANIMATION) {
|
||||
// disable animation
|
||||
setAnimationMode(NO_ANIMATION);
|
||||
// come to a halt looking settled and at peace
|
||||
String rest = getRestingAction();
|
||||
if (rest != null) {
|
||||
setActionSequence(rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The action to use when at rest. */
|
||||
protected String _restingAction = STANDING;
|
||||
|
||||
/** The action to use when following a path. */
|
||||
protected String _followingPathAction = WALKING;
|
||||
|
||||
/** A reference to the descriptor for the character that we're
|
||||
* visualizing. */
|
||||
protected CharacterDescriptor _descrip;
|
||||
|
||||
/** A reference to the character manager that created us. */
|
||||
protected CharacterManager _charmgr;
|
||||
|
||||
/** The action we are currently displaying. */
|
||||
protected String _action;
|
||||
|
||||
/** The animation frames for the active action sequence in each
|
||||
* orientation. */
|
||||
protected ActionFrames _aframes;
|
||||
|
||||
/** The offset from the upper-left of the total sprite bounds to the
|
||||
* upper-left of the image within those bounds. */
|
||||
protected Point _ioff = new Point();
|
||||
|
||||
/** The bounds of the current sprite image. */
|
||||
protected Rectangle _ibounds = new Rectangle();
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
//
|
||||
// $Id: ComponentClass.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.ComparableArrayList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Denotes a class of components to which {@link CharacterComponent}
|
||||
* objects belong. Examples include "Hat", "Head", and "Feet". A component
|
||||
* class dictates a component's rendering priority so that components can
|
||||
* be rendered in an order that causes them to overlap properly.
|
||||
*
|
||||
* <p> Components support render priority overrides for particular
|
||||
* actions, orientations or combinations of actions and orientations. The
|
||||
* system is currently structured with the expectation that the overrides
|
||||
* will be relatively few (less than fifteen, say) for any given component
|
||||
* class. A system that relied on many overrides for its components would
|
||||
* want to implement a more scalable algorithm for determining which, if
|
||||
* any, override matches a particular action and orientation combination.
|
||||
*/
|
||||
public class ComponentClass implements Serializable
|
||||
{
|
||||
/** Used to effect custom render orders for particular actions,
|
||||
* orientations, etc. */
|
||||
public static class PriorityOverride
|
||||
implements Comparable, Serializable
|
||||
{
|
||||
/** The overridden render priority value. */
|
||||
public int renderPriority;
|
||||
|
||||
/** The action, if any, for which this override is appropriate. */
|
||||
public String action;
|
||||
|
||||
/** The orientations, if any, for which this override is
|
||||
* appropriate. */
|
||||
public ArrayIntSet orients;
|
||||
|
||||
/**
|
||||
* Determines whether this priority override matches the specified
|
||||
* action and orientation combination.
|
||||
*/
|
||||
public boolean matches (String action, int orient)
|
||||
{
|
||||
return (((orients == null) || orients.contains(orient)) &&
|
||||
((this.action == null) || this.action.equals(action)));
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int compareTo (Object other)
|
||||
{
|
||||
// overrides with both an action and an orientation should
|
||||
// come first in the list
|
||||
PriorityOverride po = (PriorityOverride)other;
|
||||
int pri = priority(), opri = po.priority();
|
||||
if (pri == opri) {
|
||||
return hashCode() - po.hashCode();
|
||||
} else {
|
||||
return pri - opri;
|
||||
}
|
||||
}
|
||||
|
||||
protected int priority ()
|
||||
{
|
||||
int priority = 0;
|
||||
if (action != null) {
|
||||
priority++;
|
||||
}
|
||||
if (orients != null) {
|
||||
priority++;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[pri=" + renderPriority + ", action=" + action +
|
||||
", orients=" + orients + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** The component class name. */
|
||||
public String name;
|
||||
|
||||
/** The default render priority. */
|
||||
public int renderPriority;
|
||||
|
||||
/** The color classes to use when recoloring components of this class. May
|
||||
* be null if a system does not use recolorable components. */
|
||||
public String[] colors;
|
||||
|
||||
/** The class name of the layer from which this component class obtains a
|
||||
* mask to limit rendering to certain areas. */
|
||||
public String mask;
|
||||
|
||||
/** Indicates the class name of the shadow layer to which this component
|
||||
* class contributes a shadow. */
|
||||
public String shadow;
|
||||
|
||||
/** 1.0 for a normal component, the alpha value of the pre-composited
|
||||
* shadow for the special "shadow" component class. */
|
||||
public float shadowAlpha = 1.0f;
|
||||
|
||||
/**
|
||||
* Creates an uninitialized instance suitable for unserialization or
|
||||
* population during XML parsing.
|
||||
*/
|
||||
public ComponentClass ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority appropriate for the specified action
|
||||
* and orientation.
|
||||
*/
|
||||
public int getRenderPriority (String action, int orientation)
|
||||
{
|
||||
// because we expect there to be relatively few priority
|
||||
// overrides, we simply search linearly through the list for the
|
||||
// closest match
|
||||
int ocount = (_overrides != null) ? _overrides.size() : 0;
|
||||
for (int ii = 0; ii < ocount; ii++) {
|
||||
PriorityOverride over = (PriorityOverride)_overrides.get(ii);
|
||||
// based on the way the overrides are sorted, the first match
|
||||
// is the most specific and the one we want
|
||||
if (over.matches(action, orientation)) {
|
||||
return over.renderPriority;
|
||||
}
|
||||
}
|
||||
|
||||
return renderPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied render priority override record to this component
|
||||
* class.
|
||||
*/
|
||||
public void addPriorityOverride (PriorityOverride override)
|
||||
{
|
||||
if (_overrides == null) {
|
||||
_overrides = new ComparableArrayList();
|
||||
}
|
||||
_overrides.insertSorted(override);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component class contributes a shadow to a
|
||||
* particular shadow layer. Note: this is different from <em>being</em> a
|
||||
* shadow layer which is determined by calling {@link #isShadow}.
|
||||
*/
|
||||
public boolean isShadowed ()
|
||||
{
|
||||
return (shadow != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component class is a shadow layer rather than a
|
||||
* normal component class.
|
||||
*/
|
||||
public boolean isShadow ()
|
||||
{
|
||||
return (shadowAlpha != 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes with the same name are the same.
|
||||
*/
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other instanceof ComponentClass) {
|
||||
return name.equals(((ComponentClass)other).name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashcode is based on component class name.
|
||||
*/
|
||||
public int hashCode ()
|
||||
{
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this component class.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("[");
|
||||
buf.append("name=").append(name);
|
||||
buf.append(", pri=").append(renderPriority);
|
||||
if (colors != null) {
|
||||
buf.append(", colors=").append(StringUtil.toString(colors));
|
||||
}
|
||||
if (mask != null) {
|
||||
buf.append(", mask=").append(mask);
|
||||
}
|
||||
if (shadowAlpha != 1.0f) {
|
||||
buf.append(", shadow=").append(shadowAlpha);
|
||||
} else if (shadow != null) {
|
||||
buf.append(", shadow=").append(shadow);
|
||||
}
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/** A list of render priority overrides. */
|
||||
protected ComparableArrayList _overrides;
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 3;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// $Id: ComponentIDBroker.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
|
||||
/**
|
||||
* Brokers component ids. The component repository interface makes
|
||||
* available a collection of components based on a unique identifier. The
|
||||
* expectation is that a collection of components will be used to populate
|
||||
* a repository and in that population process, component ids will be
|
||||
* assigned to the components. The component id broker system provides a
|
||||
* means by which named components can be mapped consistently to a set of
|
||||
* component ids. Humans can then be responsible for assigning unique
|
||||
* names to the components and the broker will ensure that those names map
|
||||
* to unique ids that won't change if the repository is rebuilt from the
|
||||
* source components.
|
||||
*/
|
||||
public interface ComponentIDBroker
|
||||
{
|
||||
/**
|
||||
* Returns the unique identifier for the named component. If no
|
||||
* identifier has yet been assigned to the specified named component,
|
||||
* one should be assigned and returned.
|
||||
*
|
||||
* @param cclass the name of the class to which the component belongs.
|
||||
* @param cname the name of the component.
|
||||
*
|
||||
* @exception PersistenceException thrown if an error occurs
|
||||
* communicating with the underlying persistence mechanism used to
|
||||
* store the name to id mappings.
|
||||
*/
|
||||
public int getComponentID (String cclass, String cname)
|
||||
throws PersistenceException;
|
||||
|
||||
/**
|
||||
* When the user of a component id broker is done obtaining component
|
||||
* ids, it must call this method to give the component id broker an
|
||||
* opportunity to flush any newly created component ids back to its
|
||||
* persistent store.
|
||||
*/
|
||||
public void commit ()
|
||||
throws PersistenceException;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// $Id: ComponentRepository.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Makes available a collection of character components and associated
|
||||
* metadata. Character components are animated sequences that can be
|
||||
* composited together to create a complete character visualization
|
||||
* (imagine interchanging pairs of boots, torsos, hats, etc.).
|
||||
*/
|
||||
public interface ComponentRepository
|
||||
{
|
||||
/**
|
||||
* Returns the {@link CharacterComponent} object for the given
|
||||
* component identifier.
|
||||
*/
|
||||
public CharacterComponent getComponent (int componentId)
|
||||
throws NoSuchComponentException;
|
||||
|
||||
/**
|
||||
* Returns the {@link CharacterComponent} object with the given
|
||||
* component class and name.
|
||||
*/
|
||||
public CharacterComponent getComponent (String className, String compName)
|
||||
throws NoSuchComponentException;
|
||||
|
||||
/**
|
||||
* Returns the {@link ComponentClass} with the specified name or null
|
||||
* if none exists with that name.
|
||||
*/
|
||||
public ComponentClass getComponentClass (String className);
|
||||
|
||||
/**
|
||||
* Iterates over the {@link ComponentClass} instances representing all
|
||||
* available character component classes.
|
||||
*/
|
||||
public Iterator enumerateComponentClasses ();
|
||||
|
||||
/**
|
||||
* Iterates over the {@link ActionSequence} instances representing
|
||||
* every available action sequence.
|
||||
*/
|
||||
public Iterator enumerateActionSequences ();
|
||||
|
||||
/**
|
||||
* Iterates over the component ids of all components in the specified
|
||||
* class.
|
||||
*/
|
||||
public Iterator enumerateComponentIds (ComponentClass compClass);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// $Id: CompositedActionFrames.java 3967 2006-03-22 02:41:15Z andrzej $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.util.MultiFrameImage;
|
||||
|
||||
import com.threerings.cast.CharacterComponent;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link MultiFrameImage} interface that is used
|
||||
* to lazily create composited character frames when they are requested.
|
||||
*/
|
||||
public class CompositedActionFrames
|
||||
implements ActionFrames, DirectionCodes
|
||||
{
|
||||
/** Used to associate a {@link CharacterComponent} with its {@link
|
||||
* ActionFrames} for a particular action. */
|
||||
public static class ComponentFrames
|
||||
{
|
||||
public CharacterComponent ccomp;
|
||||
|
||||
public ActionFrames frames;
|
||||
|
||||
public ComponentFrames () {
|
||||
}
|
||||
|
||||
public ComponentFrames (
|
||||
CharacterComponent ccomp, ActionFrames frames) {
|
||||
this.ccomp = ccomp;
|
||||
this.frames = frames;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return ccomp + ":" + frames;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a set of composited action frames with the supplied
|
||||
* source frames and colorization configuration. The actual component
|
||||
* frame images will not be composited until they are requested.
|
||||
*/
|
||||
public CompositedActionFrames (ImageManager imgr, LRUHashMap frameCache,
|
||||
String action, ComponentFrames[] sources)
|
||||
{
|
||||
// sanity check
|
||||
if (sources == null || sources.length == 0) {
|
||||
String errmsg = "Requested to composite invalid set of source " +
|
||||
"frames! [action=" + action +
|
||||
", sources=" + StringUtil.toString(sources) + "].";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
|
||||
_imgr = imgr;
|
||||
_frameCache = frameCache;
|
||||
_sources = sources;
|
||||
_action = action;
|
||||
|
||||
// the sources must all have the same orientation count, so we
|
||||
// just use the first
|
||||
_orientCount = _sources[0].frames.getOrientationCount();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getOrientationCount ()
|
||||
{
|
||||
return _orientCount;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public TrimmedMultiFrameImage getFrames (int orient)
|
||||
{
|
||||
_key.setOrient(orient);
|
||||
CompositedMultiFrameImage cmfi =
|
||||
(CompositedMultiFrameImage)_frameCache.get(_key);
|
||||
if (cmfi == null) {
|
||||
cmfi = createFrames(orient);
|
||||
_frameCache.put(new CompositedFramesKey(orient), cmfi);
|
||||
}
|
||||
return cmfi;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getXOrigin (int orient, int frameIdx)
|
||||
{
|
||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
||||
getFrames(orient);
|
||||
return cmfi.getXOrigin(frameIdx);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getYOrigin (int orient, int frameIdx)
|
||||
{
|
||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
||||
getFrames(orient);
|
||||
return cmfi.getYOrigin(frameIdx);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public ActionFrames cloneColorized (Colorization[] zations)
|
||||
{
|
||||
throw new RuntimeException("What you talkin' about Willis?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates our underlying multi-frame image for a particular orientation.
|
||||
*/
|
||||
protected CompositedMultiFrameImage createFrames (int orient)
|
||||
{
|
||||
return new CompositedMultiFrameImage(_imgr, _sources, _action, orient);
|
||||
}
|
||||
|
||||
/** Used to cache composited frames for a particular action and
|
||||
* orientation. */
|
||||
protected class CompositedFramesKey
|
||||
{
|
||||
public CompositedFramesKey (int orient) {
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
public void setOrient (int orient) {
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
public CompositedActionFrames getOwner () {
|
||||
return CompositedActionFrames.this;
|
||||
}
|
||||
|
||||
public boolean equals (Object other) {
|
||||
CompositedFramesKey okey = (CompositedFramesKey)other;
|
||||
return ((getOwner() == okey.getOwner()) &&
|
||||
(_orient == okey._orient));
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return CompositedActionFrames.this.hashCode() ^ _orient;
|
||||
}
|
||||
|
||||
protected int _orient;
|
||||
}
|
||||
|
||||
/** The image manager from whom we can obtain prepared volatile images
|
||||
* onto which to render our composited actions. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** Used to cache our composited action frame images. */
|
||||
protected LRUHashMap _frameCache;
|
||||
|
||||
/** The action for which we're compositing frames. */
|
||||
protected String _action;
|
||||
|
||||
/** The number of orientations. */
|
||||
protected int _orientCount;
|
||||
|
||||
/** Our source components and action frames. */
|
||||
protected ComponentFrames[] _sources;
|
||||
|
||||
/** Used to avoid creating a new key object every time we do a cache
|
||||
* lookup. */
|
||||
protected CompositedFramesKey _key = new CompositedFramesKey(0);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Transparency;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
|
||||
/**
|
||||
* Used to composite action frames with mask frames.
|
||||
*/
|
||||
public class CompositedMaskedImage extends CompositedMultiFrameImage
|
||||
{
|
||||
public CompositedMaskedImage (
|
||||
ImageManager imgr, ComponentFrames[] sources, String action,
|
||||
int orient)
|
||||
{
|
||||
super(imgr, sources, action, orient);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getWidth(index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getHeight(index);
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _sources[0].frames.getXOrigin(_orient, index);
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _sources[0].frames.getYOrigin(_orient, index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
_images[index].paint(g, x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface TrimmedMultiFrameImage
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(_images[index].getX(), _images[index].getY(),
|
||||
_images[index].getWidth(), _images[index].getHeight());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected CompositedMirage createCompositedMirage (int index)
|
||||
{
|
||||
return new MaskedMirage(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the image in the first source with the mask in the second. */
|
||||
protected class MaskedMirage extends CompositedMirage
|
||||
{
|
||||
public MaskedMirage (int index)
|
||||
{
|
||||
super(index);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
||||
{
|
||||
if (bounds.width == 0 && bounds.height == 0) {
|
||||
bounds.setBounds(tbounds);
|
||||
} else {
|
||||
bounds = bounds.intersection(tbounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void refreshVolatileImage ()
|
||||
{
|
||||
Graphics2D g = (Graphics2D)_image.getGraphics();
|
||||
try {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[0].frames.getFrames(_orient),
|
||||
mask = _sources[1].frames.getFrames(_orient);
|
||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
g.setComposite(AlphaComposite.DstIn);
|
||||
mask.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
|
||||
} finally {
|
||||
// clean up after ourselves
|
||||
if (g != null) {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3967 2006-03-22 02:41:15Z andrzej $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Transparency;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
||||
|
||||
/**
|
||||
* Used to composite the action frames for a particular orientation of a
|
||||
* {@link CompositedActionFrames}.
|
||||
*/
|
||||
public class CompositedMultiFrameImage
|
||||
implements TrimmedMultiFrameImage
|
||||
{
|
||||
public CompositedMultiFrameImage (
|
||||
ImageManager imgr, ComponentFrames[] sources,
|
||||
String action, int orient)
|
||||
{
|
||||
_imgr = imgr;
|
||||
_sources = sources;
|
||||
_action = action;
|
||||
_orient = orient;
|
||||
|
||||
// create our frame images (which will do the compositing)
|
||||
int fcount = sources[0].frames.getFrames(orient).getFrameCount();
|
||||
_images = new CompositedMirage[fcount];
|
||||
for (int ii = 0; ii < fcount; ii++) {
|
||||
_images[ii] = createCompositedMirage(ii);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getFrameCount () {
|
||||
return _images.length;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _images[index].getWidth();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _images[index].getHeight();
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _images[index].getXOrigin();
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _images[index].getYOrigin();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
_images[index].paint(g, x, y);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x, y);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(0, 0, getWidth(index), getHeight(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated memory usage of our composited frame images.
|
||||
*/
|
||||
public long getEstimatedMemoryUsage ()
|
||||
{
|
||||
long size = 0;
|
||||
for (int ii = 0; ii < _images.length; ii++) {
|
||||
size += _images[ii].getEstimatedMemoryUsage();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a composited image for the specified frame.
|
||||
*/
|
||||
protected CompositedMirage createCompositedMirage (int index)
|
||||
{
|
||||
return new CompositedMirage(index);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Mirage getFrame (int orient, int index)
|
||||
{
|
||||
return _images[index];
|
||||
}
|
||||
|
||||
/** The image manager from whom we load our images. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** The action frames from which we obtain our source imagery. */
|
||||
protected ComponentFrames[] _sources;
|
||||
|
||||
/** The action we're compositing. */
|
||||
protected String _action;
|
||||
|
||||
/** The orientation we're compositing. */
|
||||
protected int _orient;
|
||||
|
||||
/** Our composited action frame images. */
|
||||
protected CompositedMirage[] _images;
|
||||
|
||||
/**
|
||||
* Used to create our mirage using the source action frame images.
|
||||
*/
|
||||
protected class CompositedMirage extends VolatileMirage
|
||||
implements Comparator
|
||||
{
|
||||
public CompositedMirage (int index)
|
||||
{
|
||||
super(CompositedMultiFrameImage.this._imgr,
|
||||
new Rectangle(0, 0, 0, 0));
|
||||
|
||||
// keep this for later
|
||||
_index = index;
|
||||
|
||||
// first we need to determine the bounds of the rectangle that
|
||||
// will enclose all of our various components
|
||||
Rectangle tbounds = new Rectangle();
|
||||
int scount = _sources.length;
|
||||
for (int ii = 0; ii < scount; ii++) {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[ii].frames.getFrames(_orient);
|
||||
source.getTrimmedBounds(index, tbounds);
|
||||
_bounds = combineBounds(_bounds, tbounds);
|
||||
}
|
||||
|
||||
// compute our new origin
|
||||
_origin.x = (_sources[0].frames.getXOrigin(_orient, index) -
|
||||
_bounds.x);
|
||||
_origin.y = (_sources[0].frames.getYOrigin(_orient, index) -
|
||||
_bounds.y);
|
||||
// Log.info("New origin [x=" + _origin.x + ", y=" + _origin.y + "].");
|
||||
|
||||
// render our volatile image for the first time
|
||||
createVolatileImage();
|
||||
}
|
||||
|
||||
public int getXOrigin ()
|
||||
{
|
||||
return _origin.x;
|
||||
}
|
||||
|
||||
public int getYOrigin ()
|
||||
{
|
||||
return _origin.y;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int compare (Object o1, Object o2)
|
||||
{
|
||||
ComponentFrames cf1 = (ComponentFrames)o1,
|
||||
cf2 = (ComponentFrames)o2;
|
||||
return (cf1.ccomp.componentClass.getRenderPriority(
|
||||
_action, _orient) -
|
||||
cf2.ccomp.componentClass.getRenderPriority(
|
||||
_action, _orient));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the working bounds with a new set of bounds.
|
||||
*/
|
||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
||||
{
|
||||
// the first one defines our initial bounds
|
||||
if (bounds.width == 0 && bounds.height == 0) {
|
||||
bounds.setBounds(tbounds);
|
||||
} else {
|
||||
bounds.add(tbounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected int getTransparency ()
|
||||
{
|
||||
return Transparency.BITMASK;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void refreshVolatileImage ()
|
||||
{
|
||||
// long start = System.currentTimeMillis();
|
||||
|
||||
// sort the sources appropriately for this orientation
|
||||
Arrays.sort(_sources, this);
|
||||
|
||||
// now render each of the components into a composited frame
|
||||
int scount = _sources.length;
|
||||
Graphics2D g = (Graphics2D)_image.getGraphics();
|
||||
try {
|
||||
for (int ii = 0; ii < scount; ii++) {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[ii].frames.getFrames(_orient);
|
||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
}
|
||||
} finally {
|
||||
// clean up after ourselves
|
||||
if (g != null) {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Log.info("Composited [orient=" + _orient + ", index=" + _index +
|
||||
// ", tbounds=" + StringUtil.toString(_bounds) + "].");
|
||||
|
||||
// long now = System.currentTimeMillis();
|
||||
// Log.info("Composited " + scount + " frames in " +
|
||||
// (now-start) + " millis.");
|
||||
}
|
||||
|
||||
protected int _index;
|
||||
protected Point _origin = new Point();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
|
||||
/**
|
||||
* Used to composite the special shadow action frames for a particular
|
||||
* orientation of a {@link CompositedActionFrames}.
|
||||
*/
|
||||
public class CompositedShadowImage extends CompositedMultiFrameImage
|
||||
{
|
||||
public CompositedShadowImage (ImageManager imgr, ComponentFrames[] sources,
|
||||
String action, int orient, float shadowAlpha)
|
||||
{
|
||||
super(imgr, sources, action, orient);
|
||||
|
||||
// create the appropriate alpha composite for rendering the shadow
|
||||
_shadowAlpha = AlphaComposite.getInstance(
|
||||
AlphaComposite.SRC_OVER, shadowAlpha);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getWidth(index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getHeight(index);
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _sources[0].frames.getXOrigin(_orient, index);
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _sources[0].frames.getYOrigin(_orient, index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
Composite ocomp = g.getComposite();
|
||||
g.setComposite(_shadowAlpha);
|
||||
_images[index].paint(g, x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
g.setComposite(ocomp);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface TrimmedMultiFrameImage
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(_images[index].getX(), _images[index].getY(),
|
||||
_images[index].getWidth(), _images[index].getHeight());
|
||||
}
|
||||
|
||||
/** The alpha value at which we render our shadow. */
|
||||
protected AlphaComposite _shadowAlpha;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// $Id: FrameProvider.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* Provides a mechanism where by a character component can obtain access
|
||||
* to its image frames for a particular action in an on demand manner.
|
||||
*/
|
||||
public interface FrameProvider
|
||||
{
|
||||
/**
|
||||
* Returns the animation frames (in the eight sprite directions) for
|
||||
* the specified action of the specified component. May return null if
|
||||
* the specified action does not exist for the specified component.
|
||||
*/
|
||||
public ActionFrames getFrames (
|
||||
CharacterComponent component, String action, String type);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the cast package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("cast");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// $Id: NoSuchComponentException.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* Thrown when an attempt is made to retrieve a non-existent character
|
||||
* component from the component repository.
|
||||
*/
|
||||
public class NoSuchComponentException extends Exception
|
||||
{
|
||||
public NoSuchComponentException (int componentId)
|
||||
{
|
||||
super("No such component [componentId=" + componentId + "]");
|
||||
_componentId = componentId;
|
||||
}
|
||||
|
||||
public NoSuchComponentException (
|
||||
String componentClass, String componentName)
|
||||
{
|
||||
super("No such component [class=" + componentClass +
|
||||
", name=" + componentName + "]");
|
||||
_componentId = -1;
|
||||
}
|
||||
|
||||
public int getComponentId ()
|
||||
{
|
||||
return _componentId;
|
||||
}
|
||||
|
||||
protected int _componentId;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: StandardActions.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* Actions are referenced by name and this interface defines constants for
|
||||
* standard actions and action suffixes used by the shadow and cropping
|
||||
* support.
|
||||
*/
|
||||
public interface StandardActions
|
||||
{
|
||||
/** The name of the standard standing action. */
|
||||
public static final String STANDING = "standing";
|
||||
|
||||
/** The name of the standard walking action. */
|
||||
public static final String WALKING = "walking";
|
||||
|
||||
/** A special action sub-type for shadow imagery. */
|
||||
public static final String SHADOW_TYPE = "shadow";
|
||||
|
||||
/** A special action sub-type for crop imagery. */
|
||||
public static final String CROP_TYPE = "crop";
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id: TrimmedMultiFrameImage.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.util.MultiFrameImage;
|
||||
|
||||
/**
|
||||
* Used to generate more memory efficient composited images in
|
||||
* circumstances where we have trimmed underlying component images.
|
||||
*/
|
||||
public interface TrimmedMultiFrameImage extends MultiFrameImage
|
||||
{
|
||||
/**
|
||||
* Fills in the minimum bounding rectangle for this image that
|
||||
* contains all non-transparent pixels. If this information is
|
||||
* unavailable, the bounds of the entire image may be returned in
|
||||
* exchange for improved performance.
|
||||
*/
|
||||
public void getTrimmedBounds (int index, Rectangle bounds);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// $Id: BuilderModel.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
|
||||
/**
|
||||
* The builder model represents the current state of the character the
|
||||
* user is building.
|
||||
*/
|
||||
public class BuilderModel
|
||||
{
|
||||
/**
|
||||
* Constructs a builder model.
|
||||
*/
|
||||
public BuilderModel (ComponentRepository crepo)
|
||||
{
|
||||
gatherComponentInfo(crepo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a builder model listener.
|
||||
*
|
||||
* @param l the listener.
|
||||
*/
|
||||
public void addListener (BuilderModelListener l)
|
||||
{
|
||||
if (!_listeners.contains(l)) {
|
||||
_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all model listeners that the builder model has changed.
|
||||
*/
|
||||
protected void notifyListeners (int event)
|
||||
{
|
||||
int size = _listeners.size();
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
((BuilderModelListener)_listeners.get(ii)).modelChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the available component classes.
|
||||
*/
|
||||
public List getComponentClasses ()
|
||||
{
|
||||
return Collections.unmodifiableList(_classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of components available in the specified class.
|
||||
*/
|
||||
public List getComponents (ComponentClass cclass)
|
||||
{
|
||||
List list = (List)_components.get(cclass);
|
||||
if (list == null) {
|
||||
list = new ArrayList();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected components in an array.
|
||||
*/
|
||||
public int[] getSelectedComponents ()
|
||||
{
|
||||
int[] values = new int[_selected.size()];
|
||||
Iterator iter = _selected.values().iterator();
|
||||
for (int i = 0; iter.hasNext(); i++) {
|
||||
values[i] = ((Integer)iter.next()).intValue();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected component for the given component class.
|
||||
*/
|
||||
public void setSelectedComponent (ComponentClass cclass, int cid)
|
||||
{
|
||||
_selected.put(cclass, Integer.valueOf(cid));
|
||||
notifyListeners(BuilderModelListener.COMPONENT_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers component class and component information from the
|
||||
* character manager for later reference by others.
|
||||
*/
|
||||
protected void gatherComponentInfo (ComponentRepository crepo)
|
||||
{
|
||||
// get the list of all component classes
|
||||
CollectionUtil.addAll(_classes, crepo.enumerateComponentClasses());
|
||||
|
||||
for (int ii = 0; ii < _classes.size(); ii++) {
|
||||
// get the list of components available for this class
|
||||
ComponentClass cclass = (ComponentClass)_classes.get(ii);
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Integer cid = (Integer)iter.next();
|
||||
ArrayList clist = (ArrayList)_components.get(cclass);
|
||||
if (clist == null) {
|
||||
_components.put(cclass, clist = new ArrayList());
|
||||
}
|
||||
|
||||
clist.add(cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The currently selected character components. */
|
||||
protected HashMap _selected = new HashMap();
|
||||
|
||||
/** The hashtable of available component ids for each class. */
|
||||
protected HashMap _components = new HashMap();
|
||||
|
||||
/** The list of all available component classes. */
|
||||
protected ArrayList _classes = new ArrayList();
|
||||
|
||||
/** The model listeners. */
|
||||
protected ArrayList _listeners = new ArrayList();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// $Id: BuilderModelListener.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
/**
|
||||
* The builder model listener interface should be implemented by
|
||||
* classes that would like to be notified when the builder model is
|
||||
* changed.
|
||||
*
|
||||
* @see BuilderModel
|
||||
*/
|
||||
public interface BuilderModelListener
|
||||
{
|
||||
/**
|
||||
* Called by the {@link BuilderModel} when the model is changed.
|
||||
*/
|
||||
public void modelChanged (int event);
|
||||
|
||||
/** Notification event constants. */
|
||||
public static final int COMPONENT_CHANGED = 0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// $Id: BuilderPanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import javax.swing.*;
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
import com.threerings.cast.CharacterManager;
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
|
||||
/**
|
||||
* The builder panel presents the user with an overview of a composited
|
||||
* character and facilities for altering the individual components that
|
||||
* comprise the character's display image.
|
||||
*/
|
||||
public class BuilderPanel extends JPanel
|
||||
{
|
||||
/**
|
||||
* Constructs the builder panel.
|
||||
*/
|
||||
public BuilderPanel (CharacterManager charmgr,
|
||||
ComponentRepository crepo, String cprefix)
|
||||
{
|
||||
setLayout(new VGroupLayout());
|
||||
|
||||
// give ourselves a wee bit of a border
|
||||
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
|
||||
// create the builder model
|
||||
BuilderModel model = new BuilderModel(crepo);
|
||||
|
||||
// create the component selection and sprite display panels
|
||||
JPanel sub = new JPanel(gl);
|
||||
sub.add(new ComponentPanel(model, cprefix));
|
||||
sub.add(new SpritePanel(charmgr, model));
|
||||
add(sub);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// $Id: ClassEditor.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
import com.threerings.cast.ComponentClass;
|
||||
|
||||
/**
|
||||
* The class editor displays a label and a slider that allow the user to
|
||||
* select the desired component for a given component class.
|
||||
*/
|
||||
public class ClassEditor extends JPanel implements ChangeListener
|
||||
{
|
||||
/**
|
||||
* Constructs a class editor.
|
||||
*/
|
||||
public ClassEditor (
|
||||
BuilderModel model, ComponentClass cclass, List components)
|
||||
{
|
||||
_model = model;
|
||||
_components = components;
|
||||
_cclass = cclass;
|
||||
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
setLayout(gl);
|
||||
|
||||
gl = new HGroupLayout();
|
||||
gl.setJustification(GroupLayout.LEFT);
|
||||
JPanel sub = new JPanel(gl);
|
||||
|
||||
sub.add(new JLabel(cclass.name + ": "));
|
||||
sub.add(_clabel = new JLabel("0"));
|
||||
add(sub);
|
||||
|
||||
// create the slider allowing selection of available components
|
||||
int max = components.size() - 1;
|
||||
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, max, 0);
|
||||
slider.setSnapToTicks(true);
|
||||
slider.addChangeListener(this);
|
||||
add(slider);
|
||||
|
||||
// set the starting component for this class
|
||||
setSelectedComponent(0);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void stateChanged (ChangeEvent e)
|
||||
{
|
||||
JSlider source = (JSlider)e.getSource();
|
||||
if (!source.getValueIsAdjusting()) {
|
||||
int val = source.getValue();
|
||||
// update the model with the newly selected component
|
||||
setSelectedComponent(val);
|
||||
// update the label with the new value
|
||||
_clabel.setText(Integer.toString(val));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected component in the builder model for the
|
||||
* component class associated with this editor to the component at
|
||||
* the given index in this editor's list of available components.
|
||||
*/
|
||||
protected void setSelectedComponent (int idx)
|
||||
{
|
||||
int cid = ((Integer)_components.get(idx)).intValue();
|
||||
_model.setSelectedComponent(_cclass, cid);
|
||||
}
|
||||
|
||||
/** The component class associated with this editor. */
|
||||
protected ComponentClass _cclass;
|
||||
|
||||
/** The components selectable via this editor. */
|
||||
protected List _components;
|
||||
|
||||
/** The label denoting the currently selected component index. */
|
||||
protected JLabel _clabel;
|
||||
|
||||
/** The builder model. */
|
||||
protected BuilderModel _model;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// $Id: ComponentPanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
import com.threerings.cast.Log;
|
||||
import com.threerings.cast.*;
|
||||
|
||||
/**
|
||||
* The component panel displays the available components for all
|
||||
* component classes and allows the user to choose a set of components
|
||||
* for compositing into a character image.
|
||||
*/
|
||||
public class ComponentPanel extends JPanel
|
||||
{
|
||||
/**
|
||||
* Constructs the component panel.
|
||||
*/
|
||||
public ComponentPanel (BuilderModel model, String cprefix)
|
||||
{
|
||||
setLayout(new VGroupLayout(GroupLayout.STRETCH));
|
||||
// set up a border
|
||||
setBorder(BorderFactory.createEtchedBorder());
|
||||
// add the component editors to the panel
|
||||
addClassEditors(model, cprefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds editor user interface elements for each component class to
|
||||
* allow the user to select the desired component.
|
||||
*/
|
||||
protected void addClassEditors (BuilderModel model, String cprefix)
|
||||
{
|
||||
List classes = model.getComponentClasses();
|
||||
int size = classes.size();
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
||||
if (!cclass.name.startsWith(cprefix)) {
|
||||
continue;
|
||||
}
|
||||
List ccomps = model.getComponents(cclass);
|
||||
if (ccomps.size() > 0) {
|
||||
add(new ClassEditor(model, cclass, ccomps));
|
||||
} else {
|
||||
Log.info("Not creating editor for empty class " +
|
||||
"[class=" + cclass + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: SpritePanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.CharacterDescriptor;
|
||||
import com.threerings.cast.CharacterManager;
|
||||
import com.threerings.cast.CharacterSprite;
|
||||
import com.threerings.cast.StandardActions;
|
||||
|
||||
/**
|
||||
* The sprite panel displays a character sprite centered in the panel
|
||||
* suitable for user perusal.
|
||||
*/
|
||||
public class SpritePanel extends JPanel
|
||||
implements DirectionCodes, BuilderModelListener
|
||||
{
|
||||
/**
|
||||
* Constructs the sprite panel.
|
||||
*/
|
||||
public SpritePanel (CharacterManager charmgr, BuilderModel model)
|
||||
{
|
||||
// save off references
|
||||
_charmgr = charmgr;
|
||||
_model = model;
|
||||
|
||||
// listen to the builder model so that we can update the
|
||||
// sprite when a new component is selected
|
||||
_model.addListener(this);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintComponent (Graphics g)
|
||||
{
|
||||
super.paintComponent(g);
|
||||
Graphics2D gfx = (Graphics2D)g;
|
||||
|
||||
if (_sprite != null) {
|
||||
// render the sprite
|
||||
_sprite.paint(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void doLayout ()
|
||||
{
|
||||
super.doLayout();
|
||||
generateSprite();
|
||||
centerSprite();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void modelChanged (int event)
|
||||
{
|
||||
if (event == COMPONENT_CHANGED) {
|
||||
generateSprite();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new character sprite for display to reflect the
|
||||
* currently selected character components.
|
||||
*/
|
||||
protected void generateSprite ()
|
||||
{
|
||||
int components[] = _model.getSelectedComponents();
|
||||
CharacterDescriptor desc = new CharacterDescriptor(components, null);
|
||||
CharacterSprite sprite = _charmgr.getCharacter(desc);
|
||||
setSprite(sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sprite to be displayed.
|
||||
*/
|
||||
protected void setSprite (CharacterSprite sprite)
|
||||
{
|
||||
sprite.setActionSequence(StandardActions.STANDING);
|
||||
sprite.setOrientation(WEST);
|
||||
_sprite = sprite;
|
||||
centerSprite();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sprite's location to render it centered within the panel.
|
||||
*/
|
||||
protected void centerSprite ()
|
||||
{
|
||||
if (_sprite != null) {
|
||||
Dimension d = getSize();
|
||||
int swid = _sprite.getWidth(), shei = _sprite.getHeight();
|
||||
int x = d.width / 2, y = (d.height + shei) / 2;
|
||||
_sprite.setLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** The sprite displayed by the panel. */
|
||||
protected CharacterSprite _sprite;
|
||||
|
||||
/** The character manager. */
|
||||
protected CharacterManager _charmgr;
|
||||
|
||||
/** The builder model. */
|
||||
protected BuilderModel _model;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// $Id: BundleUtil.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
|
||||
import com.threerings.cast.Log;
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Utility functions related to creating and manipulating component
|
||||
* bundles.
|
||||
*/
|
||||
public class BundleUtil
|
||||
{
|
||||
/** The path in the metadata bundle to the serialized action table. */
|
||||
public static final String ACTIONS_PATH = "actions.dat";
|
||||
|
||||
/** The path in the metadata bundle to the serialized action tile sets
|
||||
* table. */
|
||||
public static final String ACTION_SETS_PATH = "action_sets.dat";
|
||||
|
||||
/** The path in the metadata bundle to the serialized component class
|
||||
* table. */
|
||||
public static final String CLASSES_PATH = "classes.dat";
|
||||
|
||||
/** The path in the component bundle to the serialized component id to
|
||||
* class/type mapping. */
|
||||
public static final String COMPONENTS_PATH = "components.dat";
|
||||
|
||||
/** The file extension of our action tile images. */
|
||||
public static final String IMAGE_EXTENSION = ".png";
|
||||
|
||||
/** The serialized tileset extension for our action tilesets. */
|
||||
public static final String TILESET_EXTENSION = ".dat";
|
||||
|
||||
/**
|
||||
* Attempts to load an object from the supplied resource bundle with
|
||||
* the specified path.
|
||||
*
|
||||
* @param wipeBundleOnFailure if there is an error reading the object
|
||||
* from the bundle and this parameter is true, we will instruct the
|
||||
* bundle to delete its underlying jar file before propagating the
|
||||
* exception with the expectation that it will be redownloaded and
|
||||
* repaired the next time the application is run.
|
||||
*
|
||||
* @return the unserialized object in question.
|
||||
*
|
||||
* @exception IOException thrown if an I/O error occurs while reading
|
||||
* the object from the bundle.
|
||||
*/
|
||||
public static Object loadObject (ResourceBundle bundle, String path,
|
||||
boolean wipeBundleOnFailure)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
InputStream bin = null;
|
||||
try {
|
||||
bin = bundle.getResource(path);
|
||||
if (bin == null) {
|
||||
return null;
|
||||
}
|
||||
return new ObjectInputStream(bin).readObject();
|
||||
|
||||
} catch (InvalidClassException ice) {
|
||||
Log.warning("Aiya! Serialized object is hosed " +
|
||||
"[bundle=" + bundle.getSource().getPath() +
|
||||
", element=" + path +
|
||||
", error=" + ice.getMessage() + "].");
|
||||
return null;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Error reading resource from bundle " +
|
||||
"[bundle=" + bundle + ", path=" + path +
|
||||
", wiping?=" + wipeBundleOnFailure + "].");
|
||||
if (wipeBundleOnFailure) {
|
||||
StreamUtil.close(bin);
|
||||
bin = null;
|
||||
bundle.wipeBundle(false);
|
||||
}
|
||||
throw ioe;
|
||||
|
||||
} finally {
|
||||
StreamUtil.close(bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
//
|
||||
// $Id: BundledComponentRepository.java 4023 2006-04-14 21:52:06Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.IntIntMap;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import org.apache.commons.collections.iterators.FilterIterator;
|
||||
import org.apache.commons.collections.Predicate;
|
||||
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
import com.threerings.resource.ResourceManager;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.FastImageIO;
|
||||
import com.threerings.media.image.ImageDataProvider;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
|
||||
import com.threerings.media.tile.IMImageProvider;
|
||||
import com.threerings.media.tile.Tile;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TrimmedTile;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.ActionFrames;
|
||||
import com.threerings.cast.ActionSequence;
|
||||
import com.threerings.cast.CharacterComponent;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
import com.threerings.cast.FrameProvider;
|
||||
import com.threerings.cast.Log;
|
||||
import com.threerings.cast.NoSuchComponentException;
|
||||
import com.threerings.cast.StandardActions;
|
||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
||||
|
||||
/**
|
||||
* A component repository implementation that obtains information from
|
||||
* resource bundles.
|
||||
*
|
||||
* @see ResourceManager
|
||||
*/
|
||||
public class BundledComponentRepository
|
||||
implements DirectionCodes, ComponentRepository
|
||||
{
|
||||
/**
|
||||
* Constructs a repository which will obtain its resource set from the
|
||||
* supplied resource manager.
|
||||
*
|
||||
* @param rmgr the resource manager from which to obtain our resource
|
||||
* set.
|
||||
* @param imgr the image manager that we'll use to decode and cache
|
||||
* images.
|
||||
* @param name the name of the resource set from which we will be
|
||||
* loading our component data.
|
||||
*
|
||||
* @exception IOException thrown if an I/O error occurs while reading
|
||||
* our metadata from the resource bundles.
|
||||
*/
|
||||
public BundledComponentRepository (
|
||||
ResourceManager rmgr, ImageManager imgr, String name)
|
||||
throws IOException
|
||||
{
|
||||
// keep this guy around
|
||||
_imgr = imgr;
|
||||
|
||||
// first we obtain the resource set from whence will come our
|
||||
// bundles
|
||||
ResourceBundle[] rbundles = rmgr.getResourceSet(name);
|
||||
|
||||
// look for our metadata info in each of the bundles
|
||||
try {
|
||||
int rcount = (rbundles == null) ? 0 : rbundles.length;
|
||||
for (int i = 0; i < rcount; i++) {
|
||||
if (_actions == null) {
|
||||
_actions = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.ACTIONS_PATH, true);
|
||||
}
|
||||
if (_actionSets == null) {
|
||||
_actionSets = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.ACTION_SETS_PATH, true);
|
||||
}
|
||||
if (_classes == null) {
|
||||
_classes = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.CLASSES_PATH, true);
|
||||
}
|
||||
}
|
||||
|
||||
// now go back and load up all of the component information
|
||||
for (int i = 0; i < rcount; i++) {
|
||||
HashIntMap comps = null;
|
||||
comps = (HashIntMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.COMPONENTS_PATH, true);
|
||||
if (comps == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a frame provider for this bundle
|
||||
FrameProvider fprov =
|
||||
new ResourceBundleProvider(_imgr, rbundles[i]);
|
||||
|
||||
// now create character component instances for each component
|
||||
// in the serialized table
|
||||
Iterator iter = comps.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
int componentId = ((Integer)iter.next()).intValue();
|
||||
Tuple info = (Tuple)comps.get(componentId);
|
||||
createComponent(componentId, (String)info.left,
|
||||
(String)info.right, fprov);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
throw (IOException) new IOException(
|
||||
"Internal error unserializing metadata").initCause(cnfe);
|
||||
}
|
||||
|
||||
// if we failed to load our classes or actions, create empty
|
||||
// hashtables so that we can safely enumerate our emptiness
|
||||
if (_actions == null) {
|
||||
_actions = new HashMap();
|
||||
}
|
||||
if (_classes == null) {
|
||||
_classes = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bundled component repository to wipe any bundles
|
||||
* that report certain kinds of failure. In the event that an unpacked
|
||||
* bundle becomes corrupt, this is useful in that it will force the
|
||||
* bundle to be unpacked on the next application invocation,
|
||||
* potentially remedying the problem of a corrupt unpacking.
|
||||
*/
|
||||
public void setWipeOnFailure (boolean wipeOnFailure)
|
||||
{
|
||||
_wipeOnFailure = true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public CharacterComponent getComponent (int componentId)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
CharacterComponent component = (CharacterComponent)
|
||||
_components.get(componentId);
|
||||
if (component == null) {
|
||||
throw new NoSuchComponentException(componentId);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public CharacterComponent getComponent (String className, String compName)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
// look up the list for that class
|
||||
ArrayList comps = (ArrayList)_classComps.get(className);
|
||||
if (comps != null) {
|
||||
// scan the list for the named component
|
||||
int ccount = comps.size();
|
||||
for (int i = 0; i < ccount; i++) {
|
||||
CharacterComponent comp = (CharacterComponent)comps.get(i);
|
||||
if (comp.name.equals(compName)) {
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NoSuchComponentException(className, compName);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public ComponentClass getComponentClass (String className)
|
||||
{
|
||||
return (ComponentClass)_classes.get(className);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Iterator enumerateComponentClasses ()
|
||||
{
|
||||
return _classes.values().iterator();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Iterator enumerateActionSequences ()
|
||||
{
|
||||
return _actions.values().iterator();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Iterator enumerateComponentIds (final ComponentClass compClass)
|
||||
{
|
||||
Predicate classP = new Predicate() {
|
||||
public boolean evaluate (Object input) {
|
||||
CharacterComponent comp = (CharacterComponent)
|
||||
_components.get(input);
|
||||
return comp.componentClass.equals(compClass);
|
||||
}
|
||||
};
|
||||
return new FilterIterator(_components.keySet().iterator(), classP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a component and inserts it into the component table.
|
||||
*/
|
||||
protected void createComponent (
|
||||
int componentId, String cclass, String cname, FrameProvider fprov)
|
||||
{
|
||||
// look up the component class information
|
||||
ComponentClass clazz = (ComponentClass)_classes.get(cclass);
|
||||
if (clazz == null) {
|
||||
Log.warning("Non-existent component class " +
|
||||
"[class=" + cclass + ", name=" + cname +
|
||||
", id=" + componentId + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// create the component
|
||||
CharacterComponent component = new CharacterComponent(
|
||||
componentId, cname, clazz, fprov);
|
||||
|
||||
// stick it into the appropriate tables
|
||||
_components.put(componentId, component);
|
||||
|
||||
// we have a hash of lists for mapping components by class/name
|
||||
ArrayList comps = (ArrayList)_classComps.get(cclass);
|
||||
if (comps == null) {
|
||||
comps = new ArrayList();
|
||||
_classComps.put(cclass, comps);
|
||||
}
|
||||
if (!comps.contains(component)) {
|
||||
comps.add(component);
|
||||
} else {
|
||||
Log.info("Requested to register the same component twice? " +
|
||||
"[component=" + component + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of these provide images to our component action tilesets
|
||||
* and frames to our components.
|
||||
*/
|
||||
protected class ResourceBundleProvider extends IMImageProvider
|
||||
implements ImageDataProvider, FrameProvider
|
||||
{
|
||||
/**
|
||||
* Constructs an instance that will obtain image data from the
|
||||
* specified resource bundle.
|
||||
*/
|
||||
public ResourceBundleProvider (ImageManager imgr, ResourceBundle bundle)
|
||||
{
|
||||
super(imgr, (String)null);
|
||||
_dprov = this;
|
||||
_bundle = bundle;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public String getIdent ()
|
||||
{
|
||||
return "bcr:" + _bundle.getSource();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public BufferedImage loadImage (String path) throws IOException
|
||||
{
|
||||
return FastImageIO.read(_bundle.getResourceFile(path));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public ActionFrames getFrames (
|
||||
CharacterComponent component, String action, String type)
|
||||
{
|
||||
// obtain the action sequence definition for this action
|
||||
ActionSequence actseq = (ActionSequence)_actions.get(action);
|
||||
if (actseq == null) {
|
||||
Log.warning("Missing action sequence definition " +
|
||||
"[action=" + action +
|
||||
", component=" + component + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine our image path name
|
||||
String imgpath = action, dimgpath = ActionSequence.DEFAULT_SEQUENCE;
|
||||
if (type != null) {
|
||||
imgpath += "_" + type;
|
||||
dimgpath += "_" + type;
|
||||
}
|
||||
|
||||
String root = component.componentClass.name + "/" +
|
||||
component.name + "/";
|
||||
String cpath = root + imgpath + BundleUtil.TILESET_EXTENSION;
|
||||
String dpath = root + dimgpath + BundleUtil.TILESET_EXTENSION;
|
||||
|
||||
// look to see if this tileset is already cached (as the
|
||||
// custom action or the default action)
|
||||
TileSet aset = (TileSet)_setcache.get(cpath);
|
||||
if (aset == null) {
|
||||
aset = (TileSet)_setcache.get(dpath);
|
||||
if (aset != null) {
|
||||
// save ourselves a lookup next time
|
||||
_setcache.put(cpath, aset);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// then try loading up a tileset customized for this action
|
||||
if (aset == null) {
|
||||
aset = (TileSet)BundleUtil.loadObject(
|
||||
_bundle, cpath, false);
|
||||
}
|
||||
|
||||
// if that failed, try loading the default tileset
|
||||
if (aset == null) {
|
||||
aset = (TileSet)BundleUtil.loadObject(
|
||||
_bundle, dpath, false);
|
||||
_setcache.put(dpath, aset);
|
||||
}
|
||||
|
||||
// if that failed too, we're hosed
|
||||
if (aset == null) {
|
||||
// if this is a shadow image, no need to freak out as they
|
||||
// are optional
|
||||
if (!StandardActions.SHADOW_TYPE.equals(type)) {
|
||||
Log.warning("Unable to locate tileset for action '" +
|
||||
imgpath + "' " + component + ".");
|
||||
if (_wipeOnFailure) {
|
||||
_bundle.wipeBundle(false);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
aset.setImageProvider(this);
|
||||
_setcache.put(cpath, aset);
|
||||
return new TileSetFrameImage(aset, actseq);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error loading tileset for action '" + imgpath +
|
||||
"' " + component + ".");
|
||||
Log.logStackTrace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The resource bundle from which we obtain image data. */
|
||||
protected ResourceBundle _bundle;
|
||||
|
||||
/** Cache of tilesets loaded from our bundle. */
|
||||
protected HashMap _setcache = new HashMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to provide multiframe images using data obtained from a
|
||||
* tileset.
|
||||
*/
|
||||
protected static class TileSetFrameImage implements ActionFrames
|
||||
{
|
||||
/**
|
||||
* Constructs a tileset frame image with the specified tileset and
|
||||
* for the specified orientation.
|
||||
*/
|
||||
public TileSetFrameImage (TileSet set, ActionSequence actseq)
|
||||
{
|
||||
_set = set;
|
||||
_actseq = actseq;
|
||||
|
||||
// compute these now to avoid pointless recomputation later
|
||||
_ocount = actseq.orients.length;
|
||||
_fcount = set.getTileCount() / _ocount;
|
||||
|
||||
// create our mapping from orientation to animation sequence
|
||||
// index
|
||||
for (int ii = 0; ii < _ocount; ii++) {
|
||||
_orients.put(actseq.orients[ii], ii);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getOrientationCount ()
|
||||
{
|
||||
return _ocount;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public TrimmedMultiFrameImage getFrames (final int orient)
|
||||
{
|
||||
return new TrimmedMultiFrameImage() {
|
||||
// documentation inherited
|
||||
public int getFrameCount ()
|
||||
{
|
||||
return _fcount;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile == null) ? 0 : tile.getWidth();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile == null) ? 0 : tile.getHeight();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
if (tile != null) {
|
||||
tile.paint(g, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile != null) ? tile.hitTest(x, y) : false;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getTrimmedBounds (int index, Rectangle bounds)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
if (tile instanceof TrimmedTile) {
|
||||
((TrimmedTile)tile).getTrimmedBounds(bounds);
|
||||
} else {
|
||||
bounds.setBounds(
|
||||
0, 0, tile.getWidth(), tile.getHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getXOrigin (int orient, int index)
|
||||
{
|
||||
return _actseq.origin.x;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getYOrigin (int orient, int index)
|
||||
{
|
||||
return _actseq.origin.y;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public ActionFrames cloneColorized (Colorization[] zations)
|
||||
{
|
||||
return new TileSetFrameImage(_set.clone(zations), _actseq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the requested tile.
|
||||
*/
|
||||
protected Tile getTile (int orient, int index)
|
||||
{
|
||||
int tileIndex = _orients.get(orient) * _fcount + index;
|
||||
return _set.getTile(tileIndex);
|
||||
}
|
||||
|
||||
/** The tileset from which we obtain our frame images. */
|
||||
protected TileSet _set;
|
||||
|
||||
/** The action sequence for which we're providing frame images. */
|
||||
protected ActionSequence _actseq;
|
||||
|
||||
/** Frame and orientation counts. */
|
||||
protected int _fcount, _ocount;
|
||||
|
||||
/** A mapping from orientation code to animation sequence
|
||||
* index. */
|
||||
protected IntIntMap _orients = new IntIntMap();
|
||||
}
|
||||
|
||||
/** We use the image manager to decode and cache images. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** A table of action sequences. */
|
||||
protected HashMap _actions;
|
||||
|
||||
/** A table of action sequence tilesets. */
|
||||
protected HashMap _actionSets;
|
||||
|
||||
/** A table of component classes. */
|
||||
protected HashMap _classes;
|
||||
|
||||
/** A table of component lists indexed on classname. */
|
||||
protected HashMap _classComps = new HashMap();
|
||||
|
||||
/** The component table. */
|
||||
protected HashIntMap _components = new HashIntMap();
|
||||
|
||||
/** Whether or not we wipe our bundles on any failure. */
|
||||
protected boolean _wipeOnFailure;
|
||||
}
|
||||
@@ -0,0 +1,624 @@
|
||||
//
|
||||
// $Id: ComponentBundlerTask.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.ComparableArrayList;
|
||||
import com.samskivert.util.FileUtil;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
||||
import com.threerings.media.tile.ImageProvider;
|
||||
import com.threerings.media.tile.SimpleCachingImageProvider;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TrimmedTileSet;
|
||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
||||
|
||||
import com.threerings.cast.ComponentIDBroker;
|
||||
import com.threerings.cast.StandardActions;
|
||||
import com.threerings.cast.bundle.BundleUtil;
|
||||
import com.threerings.cast.tools.xml.ActionRuleSet;
|
||||
|
||||
/**
|
||||
* Ant task for creating component bundles. This task must be configured
|
||||
* with a number of parameters:
|
||||
*
|
||||
* <pre>
|
||||
* target=[path to bundle file, which will be created]
|
||||
* mapfile=[path to the component map file which maintains a mapping from
|
||||
* component id to component class/name, it will be created the
|
||||
* first time and updated as new components are mapped]
|
||||
* </pre>
|
||||
*
|
||||
* It should also contain one or more nested <fileset> elements that
|
||||
* enumerate the action tileset images that should be included in the
|
||||
* component bundle.
|
||||
*/
|
||||
public class ComponentBundlerTask extends Task
|
||||
{
|
||||
/**
|
||||
* Sets the path to the bundle file that we'll be creating.
|
||||
*/
|
||||
public void setTarget (File target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the component map file that we'll use to obtain
|
||||
* component ids for the bundled components.
|
||||
*/
|
||||
public void setMapfile (File mapfile)
|
||||
{
|
||||
_mapfile = mapfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the action tilesets definition file.
|
||||
*/
|
||||
public void setActiondef (File actiondef)
|
||||
{
|
||||
_actionDef = actiondef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root path which will be stripped from the image paths
|
||||
* prior to parsing them to obtain the component class, type and
|
||||
* action names.
|
||||
*/
|
||||
public void setRoot (File root)
|
||||
{
|
||||
_root = root.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a nested <fileset> element.
|
||||
*/
|
||||
public void addFileset (FileSet set)
|
||||
{
|
||||
_filesets.add(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual work of the task.
|
||||
*/
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
// make sure everything was set up properly
|
||||
ensureSet(_target, "Must specify the path to the target bundle " +
|
||||
"file via the 'target' attribute.");
|
||||
ensureSet(_mapfile, "Must specify the path to the component map " +
|
||||
"file via the 'mapfile' attribute.");
|
||||
ensureSet(_actionDef, "Must specify the action sequence " +
|
||||
"definitions via the 'actiondef' attribute.");
|
||||
|
||||
// parse in the action tilesets
|
||||
HashMap actsets = parseActionTileSets();
|
||||
|
||||
// load up our component ID broker
|
||||
ComponentIDBroker broker = loadBroker(_mapfile);
|
||||
|
||||
// check to see if any of the source files are newer than the
|
||||
// target file
|
||||
ArrayList sources = (ArrayList)_filesets.clone();
|
||||
sources.add(_mapfile);
|
||||
sources.add(_actionDef);
|
||||
if (!outOfDate(sources, _target)) {
|
||||
System.out.println(_target.getPath() + " is up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
// create an image provider for loading our component images
|
||||
ImageProvider improv = new SimpleCachingImageProvider() {
|
||||
protected BufferedImage loadImage (String path)
|
||||
throws IOException {
|
||||
return ImageIO.read(new File(path));
|
||||
}
|
||||
};
|
||||
|
||||
System.out.println("Generating " + _target.getPath() + "...");
|
||||
|
||||
try {
|
||||
// make sure we can create our bundle file
|
||||
FileOutputStream fout = new FileOutputStream(_target);
|
||||
JarOutputStream jout = new JarOutputStream(fout);
|
||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
// we'll fill this with component id to tuple mappings
|
||||
HashIntMap mapping = new HashIntMap();
|
||||
|
||||
// herein we'll insert trimmed tileset objects that go along
|
||||
// with each of the trimmed action images
|
||||
HashIntMap actionSets = new HashIntMap();
|
||||
|
||||
// deal with the filesets
|
||||
for (int i = 0; i < _filesets.size(); i++) {
|
||||
FileSet fs = (FileSet)_filesets.get(i);
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
File cfile = new File(fromDir, srcFiles[f]);
|
||||
// determine the [class, name, action] triplet
|
||||
String[] info = decomposePath(cfile.getPath());
|
||||
|
||||
// make sure we have an action tileset definition
|
||||
TileSet aset = (TileSet)actsets.get(info[2]);
|
||||
if (aset == null) {
|
||||
System.err.println(
|
||||
"No tileset definition for component action " +
|
||||
"[class=" + info[0] + ", name=" + info[1] +
|
||||
", action=" + info[2] + "].");
|
||||
continue;
|
||||
}
|
||||
aset.setImageProvider(improv);
|
||||
|
||||
// obtain the component id from our id broker
|
||||
int cid = broker.getComponentID(info[0], info[1]);
|
||||
// add a mapping for this component
|
||||
mapping.put(cid, new Tuple(info[0], info[1]));
|
||||
|
||||
// process and store the main component image
|
||||
processComponent(info, aset, cfile, jout);
|
||||
|
||||
// pick up any auxiliary images as well like the shadow or
|
||||
// crop files
|
||||
String action = info[2];
|
||||
String ext = BundleUtil.IMAGE_EXTENSION;
|
||||
for (int aa = 0; aa < AUX_EXTS.length; aa++) {
|
||||
File afile = new File(
|
||||
FileUtil.resuffix(cfile, ext, AUX_EXTS[aa] + ext));
|
||||
if (afile.exists()) {
|
||||
info[2] = action + AUX_EXTS[aa];
|
||||
processComponent(info, aset, afile, jout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write our mapping table to the jar file as well
|
||||
jout.putNextEntry(new JarEntry(BundleUtil.COMPONENTS_PATH));
|
||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(mapping);
|
||||
oout.flush();
|
||||
|
||||
// seal up our jar file
|
||||
jout.close();
|
||||
|
||||
} catch (IOException ioe) {
|
||||
String errmsg = "Unable to create component bundle.";
|
||||
throw new BuildException(errmsg, ioe);
|
||||
|
||||
} catch (PersistenceException pe) {
|
||||
String errmsg = "Unable to obtain component ID mapping.";
|
||||
throw new BuildException(errmsg, pe);
|
||||
}
|
||||
|
||||
// save our updated component ID broker
|
||||
saveBroker(_mapfile, broker);
|
||||
}
|
||||
|
||||
protected void processComponent (
|
||||
String[] info, TileSet aset, File cfile, JarOutputStream jout)
|
||||
throws IOException
|
||||
{
|
||||
// construct the path that'll go in the jar file
|
||||
String ipath = composePath(
|
||||
info, BundleUtil.IMAGE_EXTENSION);
|
||||
jout.putNextEntry(new JarEntry(ipath));
|
||||
|
||||
// create a trimmed tileset based on the source action tileset and
|
||||
// stuff the new trimmed image into the jar file at the same time
|
||||
aset.setImagePath(cfile.getPath());
|
||||
|
||||
TrimmedTileSet tset = null;
|
||||
try {
|
||||
tset = TrimmedTileSet.trimTileSet(aset, jout);
|
||||
tset.setImagePath(ipath);
|
||||
} catch (Throwable t) {
|
||||
System.err.println(
|
||||
"Failure trimming tileset " +
|
||||
"[class=" + info[0] + ", name=" + info[1] +
|
||||
", action=" + info[2] +
|
||||
", srcimg=" + aset.getImagePath() + "].");
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
// then write our trimmed tileset to the jar file
|
||||
if (tset != null) {
|
||||
String tpath = composePath(
|
||||
info, BundleUtil.TILESET_EXTENSION);
|
||||
jout.putNextEntry(new JarEntry(tpath));
|
||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(tset);
|
||||
oout.flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean outOfDate (ArrayList sources, File target)
|
||||
{
|
||||
for (int ii = 0; ii < sources.size(); ii++) {
|
||||
if (outOfDate(sources.get(ii), target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean outOfDate (Object source, File target)
|
||||
{
|
||||
if (source instanceof FileSet) {
|
||||
FileSet fs = (FileSet)source;
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
File cfile = new File(fromDir, srcFiles[f]);
|
||||
if (newer(cfile, target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
} else if (source instanceof File) {
|
||||
return newer((File)source, target);
|
||||
|
||||
} else {
|
||||
System.err.println("Can't compare " + source +
|
||||
" to " + target + ".");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if <code>source</code> is newer than
|
||||
* <code>target</code>.
|
||||
*/
|
||||
protected boolean newer (File source, File target)
|
||||
{
|
||||
return source.lastModified() > target.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decomposes the full path to a component image into a [class, name,
|
||||
* action] triplet.
|
||||
*/
|
||||
protected String[] decomposePath (String path)
|
||||
throws BuildException
|
||||
{
|
||||
// first strip off the root
|
||||
if (!path.startsWith(_root)) {
|
||||
throw new BuildException("Can't bundle images outside the " +
|
||||
"root directory [root=" + _root +
|
||||
", path=" + path + "].");
|
||||
}
|
||||
path = path.substring(_root.length());
|
||||
|
||||
// strip off any preceding slash
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
// now strip off the file extension
|
||||
if (!path.endsWith(BundleUtil.IMAGE_EXTENSION)) {
|
||||
throw new BuildException("Can't bundle malformed image file " +
|
||||
"[path=" + path + "].");
|
||||
}
|
||||
path = path.substring(0, path.length() -
|
||||
BundleUtil.IMAGE_EXTENSION.length());
|
||||
|
||||
// now decompose the path; the component type and action must
|
||||
// always be a single string but the class can span multiple
|
||||
// directories for easier component organization; thus
|
||||
// "male/head/goatee/standing" will be parsed as [class=male/head,
|
||||
// type=goatee, action=standing]
|
||||
String malmsg = "Can't decode malformed image path: '" + path + "'";
|
||||
String[] info = new String[3];
|
||||
int lsidx = path.lastIndexOf(File.separator);
|
||||
if (lsidx == -1) {
|
||||
throw new BuildException(malmsg);
|
||||
}
|
||||
info[2] = path.substring(lsidx+1);
|
||||
int slsidx = path.lastIndexOf(File.separator, lsidx-1);
|
||||
if (slsidx == -1) {
|
||||
throw new BuildException(malmsg);
|
||||
}
|
||||
info[1] = path.substring(slsidx+1, lsidx);
|
||||
info[0] = path.substring(0, slsidx);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a triplet of [class, name, action] into the path that
|
||||
* should be supplied to the JarEntry that contains the associated
|
||||
* image data.
|
||||
*/
|
||||
protected String composePath (String[] info, String extension)
|
||||
{
|
||||
return (info[0] + File.separator + info[1] +
|
||||
File.separator + info[2] + extension);
|
||||
}
|
||||
|
||||
protected void ensureSet (Object value, String errmsg)
|
||||
throws BuildException
|
||||
{
|
||||
if (value == null) {
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the action tileset definitions and puts them into a hash
|
||||
* map, keyed on action name.
|
||||
*/
|
||||
protected HashMap parseActionTileSets ()
|
||||
{
|
||||
Digester digester = new Digester();
|
||||
SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet();
|
||||
String aprefix = "actions" + ActionRuleSet.ACTION_PATH;
|
||||
srules.setPrefix(aprefix);
|
||||
|
||||
digester.addRuleSet(srules);
|
||||
digester.addSetProperties(aprefix);
|
||||
digester.addSetNext(aprefix + SwissArmyTileSetRuleSet.TILESET_PATH,
|
||||
"addTileSet", TileSet.class.getName());
|
||||
|
||||
HashMap actsets = new ActionMap();
|
||||
digester.push(actsets);
|
||||
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_actionDef);
|
||||
BufferedInputStream bin = new BufferedInputStream(fin);
|
||||
digester.parse(bin);
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
String errmsg = "Unable to load action definition file " +
|
||||
"[path=" + _actionDef.getPath() + "].";
|
||||
throw new BuildException(errmsg, fnfe);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Parsing error.", e);
|
||||
}
|
||||
|
||||
return actsets;
|
||||
}
|
||||
|
||||
/** Used when parsing action tilesets. */
|
||||
public static class ActionMap extends HashMap
|
||||
{
|
||||
public void setName (String name) {
|
||||
_name = name;
|
||||
}
|
||||
public void addTileSet (TileSet set) {
|
||||
set.setName(_name);
|
||||
put(_name, set);
|
||||
}
|
||||
protected String _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the hashmap ID broker from its persistent representation in
|
||||
* the specified file.
|
||||
*/
|
||||
protected HashMapIDBroker loadBroker (File mapfile)
|
||||
throws BuildException
|
||||
{
|
||||
HashMapIDBroker broker = new HashMapIDBroker();
|
||||
|
||||
try {
|
||||
BufferedReader bin = new BufferedReader(new FileReader(mapfile));
|
||||
broker.readFrom(bin);
|
||||
bin.close();
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
// if the file doesn't yet exist, start with a blank broker
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Error loading component ID map " +
|
||||
"[mapfile=" + mapfile + "]", e);
|
||||
}
|
||||
|
||||
return broker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a persistent representation of the supplied hashmap ID
|
||||
* broker in the specified file.
|
||||
*/
|
||||
protected void saveBroker (File mapfile, ComponentIDBroker broker)
|
||||
throws BuildException
|
||||
{
|
||||
HashMapIDBroker hbroker = (HashMapIDBroker)broker;
|
||||
|
||||
// bail if the broker wasn't modified
|
||||
if (!hbroker.isModified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedWriter bout = new BufferedWriter(new FileWriter(mapfile));
|
||||
hbroker.writeTo(bout);
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException("Unable to store component ID map " +
|
||||
"[mapfile=" + mapfile + "]", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class HashMapIDBroker
|
||||
extends HashMap implements ComponentIDBroker
|
||||
{
|
||||
public int getComponentID (String cclass, String cname)
|
||||
throws PersistenceException
|
||||
{
|
||||
Tuple key = new Tuple(cclass, cname);
|
||||
Integer cid = (Integer)get(key);
|
||||
if (cid == null) {
|
||||
cid = Integer.valueOf(++_nextCID);
|
||||
put(key, cid);
|
||||
}
|
||||
return cid.intValue();
|
||||
}
|
||||
|
||||
public void commit ()
|
||||
throws PersistenceException
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
public boolean isModified ()
|
||||
{
|
||||
return _nextCID != _startCID;
|
||||
}
|
||||
|
||||
public void writeTo (BufferedWriter bout)
|
||||
throws IOException
|
||||
{
|
||||
// write out our most recently assigned component id
|
||||
String cidline = "" + _nextCID;
|
||||
bout.write(cidline, 0, cidline.length());
|
||||
bout.newLine();
|
||||
|
||||
// write out the keys and values
|
||||
ComparableArrayList lines = new ComparableArrayList();
|
||||
Iterator keys = keySet().iterator();
|
||||
while (keys.hasNext()) {
|
||||
Tuple key = (Tuple)keys.next();
|
||||
Integer value = (Integer)get(key);
|
||||
String line = key.left + SEP_STR + key.right + SEP_STR + value;
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
// sort the output
|
||||
lines.sort();
|
||||
|
||||
// now write it to the file
|
||||
int lcount = lines.size();
|
||||
for (int ii = 0; ii < lcount; ii++) {
|
||||
String line = (String)lines.get(ii);
|
||||
bout.write(line, 0, line.length());
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
public void readFrom (BufferedReader bin)
|
||||
throws IOException
|
||||
{
|
||||
// read in our most recently assigned component id
|
||||
_nextCID = readInt(bin);
|
||||
// keep track of this so that we can tell if we were modified
|
||||
_startCID = _nextCID;
|
||||
|
||||
// now read in our keys and values
|
||||
String line;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
String orig = line;
|
||||
int sidx = line.indexOf(SEP_STR);
|
||||
if (sidx == -1) {
|
||||
throw new IOException("Malformed line '" + orig + "'");
|
||||
}
|
||||
String cclass = line.substring(0, sidx);
|
||||
line = line.substring(sidx + SEP_STR.length());
|
||||
sidx = line.indexOf(SEP_STR);
|
||||
if (sidx == -1) {
|
||||
throw new IOException("Malformed line '" + orig + "'");
|
||||
}
|
||||
String cname = line.substring(0, sidx);
|
||||
line = line.substring(sidx + SEP_STR.length());
|
||||
try {
|
||||
put(new Tuple(cclass, cname), Integer.valueOf(line));
|
||||
} catch (NumberFormatException nfe) {
|
||||
String err = "Malformed line, invalid code '" + orig + "'";
|
||||
throw new IOException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int readInt (BufferedReader bin)
|
||||
throws IOException
|
||||
{
|
||||
String line = bin.readLine();
|
||||
try {
|
||||
return Integer.parseInt(line);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IOException("Expected number, got '" + line + "'");
|
||||
}
|
||||
}
|
||||
|
||||
protected int _nextCID = 0;
|
||||
protected int _startCID = 0;
|
||||
}
|
||||
|
||||
/** The path to our component bundle file. */
|
||||
protected File _target;
|
||||
|
||||
/** The path to our component map file. */
|
||||
protected File _mapfile;
|
||||
|
||||
/** The path to our action tilesets definition file. */
|
||||
protected File _actionDef;
|
||||
|
||||
/** The component directory root. */
|
||||
protected String _root;
|
||||
|
||||
/** A list of filesets that contain tile images. */
|
||||
protected ArrayList _filesets = new ArrayList();
|
||||
|
||||
/** Used to separate keys and values in the map file. */
|
||||
protected static final String SEP_STR = " := ";
|
||||
|
||||
/** Used to process auxilliary tilesets. */
|
||||
protected static final String[] AUX_EXTS = {
|
||||
"_" + StandardActions.SHADOW_TYPE,
|
||||
"_" + StandardActions.CROP_TYPE };
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// $Id: DumpBundle.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
import com.threerings.cast.bundle.BundleUtil;
|
||||
|
||||
/**
|
||||
* Dumps the contents of a component bundle to stdout.
|
||||
*/
|
||||
public class DumpBundle
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 1) {
|
||||
String usage = "Usage: DumpBundle bundle.jar [bundle.jar ...]";
|
||||
System.err.println(usage);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
File file = new File(args[i]);
|
||||
try {
|
||||
ResourceBundle bundle = new ResourceBundle(file);
|
||||
|
||||
HashMap actions = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.ACTIONS_PATH, false);
|
||||
dumpTable("actions: ", actions);
|
||||
|
||||
HashMap actionSets = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.ACTION_SETS_PATH, false);
|
||||
dumpTable("actionSets: ", actionSets);
|
||||
|
||||
HashMap classes = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.CLASSES_PATH, false);
|
||||
dumpTable("classes: ", classes);
|
||||
|
||||
HashIntMap comps = (HashIntMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.COMPONENTS_PATH, false);
|
||||
dumpTable("components: ", comps);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error dumping bundle [path=" + args[i] +
|
||||
", error=" + e + "].");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void dumpTable (String prefix, Map table)
|
||||
{
|
||||
if (table != null) {
|
||||
Iterator iter = table.entrySet().iterator();
|
||||
System.out.println(prefix + StringUtil.toString(iter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// $Id: MetadataBundlerTask.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
||||
|
||||
import com.threerings.cast.ActionSequence;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.cast.bundle.BundleUtil;
|
||||
|
||||
import com.threerings.cast.tools.xml.ActionRuleSet;
|
||||
import com.threerings.cast.tools.xml.ClassRuleSet;
|
||||
|
||||
/**
|
||||
* Ant task for creating metadata bundles, which contain action sequence
|
||||
* and component class definition information. This task must be
|
||||
* configured with a number of parameters:
|
||||
*
|
||||
* <pre>
|
||||
* actiondef=[path to actions.xml]
|
||||
* classdef=[path to classes.xml]
|
||||
* file=[path to metadata bundle, which will be created]
|
||||
* </pre>
|
||||
*/
|
||||
public class MetadataBundlerTask extends Task
|
||||
{
|
||||
public void setActiondef (String actiondef)
|
||||
{
|
||||
_actionDef = actiondef;
|
||||
}
|
||||
|
||||
public void setClassdef (String classdef)
|
||||
{
|
||||
_classDef = classdef;
|
||||
}
|
||||
|
||||
public void setTarget (File target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual work of the task.
|
||||
*/
|
||||
public void execute ()
|
||||
throws BuildException
|
||||
{
|
||||
// make sure everythign was set up properly
|
||||
ensureSet(_actionDef, "Must specify the action sequence " +
|
||||
"definitions via the 'actiondef' attribute.");
|
||||
ensureSet(_classDef, "Must specify the component class definitions " +
|
||||
"via the 'classdef' attribute.");
|
||||
ensureSet(_target, "Must specify the path to the target bundle " +
|
||||
"file via the 'target' attribute.");
|
||||
|
||||
// make sure we can write to the target bundle file
|
||||
FileOutputStream fout = null;
|
||||
try {
|
||||
fout = new FileOutputStream(_target);
|
||||
|
||||
// parse our metadata
|
||||
Tuple tuple = parseActions();
|
||||
HashMap actions = (HashMap)tuple.left;
|
||||
HashMap actionSets = (HashMap)tuple.right;
|
||||
HashMap classes = parseClasses();
|
||||
|
||||
// and create the bundle file
|
||||
JarOutputStream jout = new JarOutputStream(fout);
|
||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
// throw the serialized actions table in there
|
||||
JarEntry aentry = new JarEntry(BundleUtil.ACTIONS_PATH);
|
||||
jout.putNextEntry(aentry);
|
||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(actions);
|
||||
oout.flush();
|
||||
|
||||
// throw the serialized action tilesets table in there
|
||||
JarEntry sentry = new JarEntry(BundleUtil.ACTION_SETS_PATH);
|
||||
jout.putNextEntry(sentry);
|
||||
oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(actionSets);
|
||||
oout.flush();
|
||||
|
||||
// throw the serialized classes table in there
|
||||
JarEntry centry = new JarEntry(BundleUtil.CLASSES_PATH);
|
||||
jout.putNextEntry(centry);
|
||||
oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(classes);
|
||||
oout.flush();
|
||||
|
||||
// close it up and we're done
|
||||
jout.close();
|
||||
|
||||
} catch (IOException ioe) {
|
||||
String errmsg = "Unable to output to target bundle " +
|
||||
"[path=" + _target.getPath() + "].";
|
||||
throw new BuildException(errmsg, ioe);
|
||||
|
||||
} finally {
|
||||
if (fout != null) {
|
||||
try {
|
||||
fout.close();
|
||||
} catch (IOException ioe) {
|
||||
// nothing to complain about here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Tuple parseActions ()
|
||||
throws BuildException
|
||||
{
|
||||
// scan through the XML once to read the actions
|
||||
Digester digester = new Digester();
|
||||
ActionRuleSet arules = new ActionRuleSet();
|
||||
arules.setPrefix("actions");
|
||||
digester.addRuleSet(arules);
|
||||
digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH,
|
||||
"add", Object.class.getName());
|
||||
ArrayList actlist = parseList(digester, _actionDef);
|
||||
|
||||
// now go through a second time reading the tileset info
|
||||
digester = new Digester();
|
||||
SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet();
|
||||
srules.setPrefix("actions" + ActionRuleSet.ACTION_PATH);
|
||||
digester.addRuleSet(srules);
|
||||
digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH +
|
||||
SwissArmyTileSetRuleSet.TILESET_PATH, "add",
|
||||
Object.class.getName());
|
||||
ArrayList setlist = parseList(digester, _actionDef);
|
||||
|
||||
// sanity check
|
||||
if (actlist.size() != setlist.size()) {
|
||||
String errmsg = "An action is missing its tileset " +
|
||||
"definition, or something even wackier is going on.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
|
||||
// now create our mappings
|
||||
HashMap actmap = new HashMap();
|
||||
HashMap setmap = new HashMap();
|
||||
|
||||
// create the action map
|
||||
for (int i = 0; i < setlist.size(); i++) {
|
||||
TileSet set = (TileSet)setlist.get(i);
|
||||
ActionSequence act = (ActionSequence)actlist.get(i);
|
||||
// make sure nothing was missing in the action sequence
|
||||
// definition parsed from XML
|
||||
String errmsg = ActionRuleSet.validate(act);
|
||||
if (errmsg != null) {
|
||||
errmsg = "Action sequence invalid [seq=" + act +
|
||||
", error=" + errmsg + "].";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
actmap.put(act.name, act);
|
||||
setmap.put(act.name, set);
|
||||
}
|
||||
|
||||
return new Tuple(actmap, setmap);
|
||||
}
|
||||
|
||||
protected HashMap parseClasses ()
|
||||
throws BuildException
|
||||
{
|
||||
// load up our action and class info
|
||||
Digester digester = new Digester();
|
||||
|
||||
// add our action rule set and a a rule to grab parsed actions
|
||||
ClassRuleSet crules = new ClassRuleSet();
|
||||
crules.setPrefix("classes");
|
||||
digester.addRuleSet(crules);
|
||||
digester.addSetNext("classes" + ClassRuleSet.CLASS_PATH,
|
||||
"add", Object.class.getName());
|
||||
|
||||
ArrayList setlist = parseList(digester, _classDef);
|
||||
HashMap clmap = new HashMap();
|
||||
|
||||
// create the action map
|
||||
for (int i = 0; i < setlist.size(); i++) {
|
||||
ComponentClass cl = (ComponentClass)setlist.get(i);
|
||||
clmap.put(cl.name, cl);
|
||||
}
|
||||
|
||||
return clmap;
|
||||
}
|
||||
|
||||
protected ArrayList parseList (Digester digester, String path)
|
||||
throws BuildException
|
||||
{
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(path);
|
||||
BufferedInputStream bin = new BufferedInputStream(fin);
|
||||
|
||||
ArrayList setlist = new ArrayList();
|
||||
digester.push(setlist);
|
||||
|
||||
// now fire up the digester to parse the stream
|
||||
try {
|
||||
digester.parse(bin);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Parsing error.", e);
|
||||
}
|
||||
|
||||
return setlist;
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
String errmsg = "Unable to load metadata definition file " +
|
||||
"[path=" + path + "].";
|
||||
throw new BuildException(errmsg, fnfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ensureSet (Object value, String errmsg)
|
||||
throws BuildException
|
||||
{
|
||||
if (value == null) {
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
protected String _actionDef;
|
||||
protected String _classDef;
|
||||
protected File _target;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// $Id: ActionRuleSet.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.tools.xml;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
import org.apache.commons.digester.RuleSetBase;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.samskivert.xml.CallMethodSpecialRule;
|
||||
import com.samskivert.xml.SetFieldRule;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
import com.threerings.cast.ActionSequence;
|
||||
|
||||
/**
|
||||
* The action rule set is used to parse the attributes of an action
|
||||
* sequence instance.
|
||||
*/
|
||||
public class ActionRuleSet extends RuleSetBase
|
||||
{
|
||||
/** The component of the digester path that is appended by the action
|
||||
* rule set to match a action. This is appended to whatever prefix is
|
||||
* provided to the action rule set to obtain the complete XML path to
|
||||
* a matched action. */
|
||||
public static final String ACTION_PATH = "/action";
|
||||
|
||||
/**
|
||||
* Instructs the action rule set to match actions with the supplied
|
||||
* prefix. For example, passing a prefix of <code>actions</code> will
|
||||
* match actions in the following XML file:
|
||||
*
|
||||
* <pre>
|
||||
* <actions>
|
||||
* <action>
|
||||
* // ...
|
||||
* </action>
|
||||
* </actions>
|
||||
* </pre>
|
||||
*
|
||||
* This must be called before adding the ruleset to a digester.
|
||||
*/
|
||||
public void setPrefix (String prefix)
|
||||
{
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the necessary rules to the digester to parse our actions.
|
||||
*/
|
||||
public void addRuleInstances (Digester digester)
|
||||
{
|
||||
// this creates the appropriate instance when we encounter a
|
||||
// <action> tag
|
||||
digester.addObjectCreate(_prefix + ACTION_PATH,
|
||||
ActionSequence.class.getName());
|
||||
|
||||
// grab the name attribute from the <action> tag
|
||||
digester.addRule(_prefix + ACTION_PATH, new SetPropertyFieldsRule());
|
||||
|
||||
// grab the other attributes from their respective tags
|
||||
digester.addRule(_prefix + ACTION_PATH + "/framesPerSecond",
|
||||
new SetFieldRule("framesPerSecond"));
|
||||
|
||||
CallMethodSpecialRule origin = new CallMethodSpecialRule() {
|
||||
public void parseAndSet (String bodyText, Object target)
|
||||
throws Exception {
|
||||
int[] coords = StringUtil.parseIntArray(bodyText);
|
||||
if (coords.length != 2) {
|
||||
String errmsg = "Invalid <origin> specification '" +
|
||||
bodyText + "'.";
|
||||
throw new Exception(errmsg);
|
||||
}
|
||||
((ActionSequence)target).origin.setLocation(
|
||||
coords[0], coords[1]);
|
||||
}
|
||||
};
|
||||
digester.addRule(_prefix + ACTION_PATH + "/origin", origin);
|
||||
|
||||
CallMethodSpecialRule orient = new CallMethodSpecialRule() {
|
||||
public void parseAndSet (String bodyText, Object target)
|
||||
throws Exception {
|
||||
ActionSequence seq = ((ActionSequence)target);
|
||||
String[] ostrs = StringUtil.parseStringArray(bodyText);
|
||||
seq.orients = new int[ostrs.length];
|
||||
for (int ii = 0; ii < ostrs.length; ii++) {
|
||||
int orient = DirectionUtil.fromShortString(ostrs[ii]);
|
||||
if (orient != DirectionCodes.NONE) {
|
||||
seq.orients[ii] = orient;
|
||||
} else {
|
||||
String errmsg = "Invalid orientation specification " +
|
||||
"[index=" + ii + ", orient=" + ostrs[ii] + "].";
|
||||
throw new Exception(errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
digester.addRule(_prefix + ACTION_PATH + "/orients", orient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all necessary fields have been parsed and set in
|
||||
* this action sequence object and are valid.
|
||||
*
|
||||
* @return null if the sequence is valid, a string explaining the
|
||||
* invalidity if it is not.
|
||||
*/
|
||||
public static String validate (ActionSequence seq)
|
||||
{
|
||||
if (StringUtil.isBlank(seq.name)) {
|
||||
return "Missing 'name' definition.";
|
||||
}
|
||||
if (seq.framesPerSecond == 0) {
|
||||
return "Missing 'framesPerSecond' definition.";
|
||||
}
|
||||
if (seq.orients == null) {
|
||||
return "Missing 'orients' definition.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The prefix at which me match our actions. */
|
||||
protected String _prefix;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// $Id: ClassRuleSet.java 3729 2005-10-13 00:00:33Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.tools.xml;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
import org.apache.commons.digester.RuleSetBase;
|
||||
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule.FieldParser;
|
||||
|
||||
import com.threerings.cast.ComponentClass.PriorityOverride;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
/**
|
||||
* The class rule set is used to parse the attributes of a component class
|
||||
* instance.
|
||||
*/
|
||||
public class ClassRuleSet extends RuleSetBase
|
||||
{
|
||||
/** The component of the digester path that is appended by the class
|
||||
* rule set to match a component class. This is appended to whatever
|
||||
* prefix is provided to the class rule set to obtain the complete XML
|
||||
* path to a matched class. */
|
||||
public static final String CLASS_PATH = "/class";
|
||||
|
||||
/**
|
||||
* Instructs the class rule set to match component classes with the
|
||||
* supplied prefix. For example, passing a prefix of
|
||||
* <code>classes</code> will match classes in the following XML file:
|
||||
*
|
||||
* <pre>
|
||||
* <classes>
|
||||
* <class .../>
|
||||
* </classes>
|
||||
* </pre>
|
||||
*
|
||||
* This must be called before adding the ruleset to a digester.
|
||||
*/
|
||||
public void setPrefix (String prefix)
|
||||
{
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the necessary rules to the digester to parse our classes.
|
||||
*/
|
||||
public void addRuleInstances (Digester digester)
|
||||
{
|
||||
// this creates the appropriate instance when we encounter a
|
||||
// <class> tag
|
||||
digester.addObjectCreate(_prefix + CLASS_PATH,
|
||||
ComponentClass.class.getName());
|
||||
|
||||
// grab the attributes from the <class> tag
|
||||
SetPropertyFieldsRule rule = new SetPropertyFieldsRule();
|
||||
rule.addFieldParser("shadowColor", new FieldParser() {
|
||||
public Object parse (String text) {
|
||||
int[] values = StringUtil.parseIntArray(text);
|
||||
return new Color(values[0], values[1], values[2], values[3]);
|
||||
}
|
||||
});
|
||||
digester.addRule(_prefix + CLASS_PATH, rule);
|
||||
|
||||
// parse render priority overrides
|
||||
String opath = _prefix + CLASS_PATH + "/override";
|
||||
digester.addObjectCreate(opath, PriorityOverride.class.getName());
|
||||
rule = new SetPropertyFieldsRule();
|
||||
rule.addFieldParser("orients", new FieldParser() {
|
||||
public Object parse (String text) {
|
||||
String[] orients = StringUtil.parseStringArray(text);
|
||||
ArrayIntSet oset = new ArrayIntSet();
|
||||
for (int ii = 0; ii < orients.length; ii++) {
|
||||
oset.add(DirectionUtil.fromShortString(orients[ii]));
|
||||
}
|
||||
return oset;
|
||||
}
|
||||
});
|
||||
digester.addRule(opath, rule);
|
||||
|
||||
digester.addSetNext(opath, "addPriorityOverride",
|
||||
PriorityOverride.class.getName());
|
||||
}
|
||||
|
||||
/** The prefix at which me match our component classes. */
|
||||
protected String _prefix;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// $Id: CastUtil.java 4188 2006-06-13 18:03:48Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
|
||||
import com.threerings.cast.CharacterDescriptor;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
import com.threerings.cast.Log;
|
||||
|
||||
/**
|
||||
* Miscellaneous cast utility routines.
|
||||
*/
|
||||
public class CastUtil
|
||||
{
|
||||
/**
|
||||
* Returns a new character descriptor populated with a random set of
|
||||
* components.
|
||||
*/
|
||||
public static CharacterDescriptor getRandomDescriptor (
|
||||
String gender, ComponentRepository crepo)
|
||||
{
|
||||
// get all available classes
|
||||
ArrayList classes = new ArrayList();
|
||||
for (int i = 0; i < CLASSES.length; i++) {
|
||||
String cname = gender + "/" + CLASSES[i];
|
||||
ComponentClass cclass = crepo.getComponentClass(cname);
|
||||
|
||||
// make sure the component class exists
|
||||
if (cclass == null) {
|
||||
Log.warning("Missing definition for component class " +
|
||||
"[class=" + cname + "].");
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure there are some components in this class
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
if (!iter.hasNext()) {
|
||||
Log.info("Skipping class for which we have no components " +
|
||||
"[class=" + cclass + "].");
|
||||
continue;
|
||||
}
|
||||
|
||||
classes.add(cclass);
|
||||
}
|
||||
|
||||
// select the components
|
||||
int size = classes.size();
|
||||
int components[] = new int[size];
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
||||
|
||||
// get the components available for this class
|
||||
ArrayList choices = new ArrayList();
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
CollectionUtil.addAll(choices, iter);
|
||||
|
||||
// choose a random component
|
||||
if (choices.size() > 0) {
|
||||
int idx = RandomUtil.getInt(choices.size());
|
||||
components[ii] = ((Integer)choices.get(idx)).intValue();
|
||||
} else {
|
||||
Log.info("Have no components in class [class=" + cclass + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return new CharacterDescriptor(components, null);
|
||||
}
|
||||
|
||||
protected static final String[] CLASSES = {
|
||||
"legs", "feet", "hand_left", "hand_right", "torso",
|
||||
"head", "hair", "hat", "eyepatch" };
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// $Id: GeomUtil.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.geom;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
/**
|
||||
* General geometry utilites.
|
||||
*/
|
||||
public class GeomUtil
|
||||
{
|
||||
/**
|
||||
* Computes and returns the dot product of the two vectors.
|
||||
*
|
||||
* @param v1s the starting point of the first vector.
|
||||
* @param v1e the ending point of the first vector.
|
||||
* @param v2s the starting point of the second vector.
|
||||
* @param v2e the ending point of the second vector.
|
||||
*/
|
||||
public static int dot (Point v1s, Point v1e, Point v2s, Point v2e)
|
||||
{
|
||||
return ((v1e.x - v1s.x) * (v2e.x - v2s.x) +
|
||||
(v1e.y - v1s.y) * (v2e.y - v2s.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the dot product of the two vectors. See
|
||||
* {@link #dot(Point,Point,Point,Point)} for an explanation of the
|
||||
* arguments
|
||||
*/
|
||||
public static int dot (int v1sx, int v1sy, int v1ex, int v1ey,
|
||||
int v2sx, int v2sy, int v2ex, int v2ey)
|
||||
{
|
||||
return ((v1ex - v1sx) * (v2ex - v2sx) + (v1ey - v1sy) * (v2ey - v2sy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the dot product of the two vectors. The
|
||||
* vectors are assumed to start with the same coordinate and end with
|
||||
* different coordinates.
|
||||
*
|
||||
* @param vs the starting point of both vectors.
|
||||
* @param v1e the ending point of the first vector.
|
||||
* @param v2e the ending point of the second vector.
|
||||
*/
|
||||
public static int dot (Point vs, Point v1e, Point v2e)
|
||||
{
|
||||
return ((v1e.x - vs.x) * (v2e.x - vs.x) +
|
||||
(v1e.y - vs.y) * (v2e.y - vs.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the dot product of the two vectors.
|
||||
* See {@link #dot(Point,Point,Point)} for an explanation of the
|
||||
* arguments
|
||||
*/
|
||||
public static int dot (int vsx, int vsy, int v1ex, int v1ey,
|
||||
int v2ex, int v2ey)
|
||||
{
|
||||
return ((v1ex - vsx) * (v2ex - vsx) + (v1ey - vsy) * (v2ey - vsy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the point nearest to the specified point <code>p3</code>
|
||||
* on the line defined by the two points <code>p1</code> and
|
||||
* <code>p2</code>. The computed point is stored into <code>n</code>.
|
||||
* <em>Note:</em> <code>p1</code> and <code>p2</code> must not be
|
||||
* coincident.
|
||||
*
|
||||
* @param p1 one point on the line.
|
||||
* @param p2 another point on the line (not equal to <code>p1</code>).
|
||||
* @param p3 the point to which we wish to be most near.
|
||||
* @param n the point on the line defined by <code>p1</code> and
|
||||
* <code>p2</code> that is nearest to <code>p</code>.
|
||||
*
|
||||
* @return the point object supplied via <code>n</code>.
|
||||
*/
|
||||
public static Point nearestToLine (Point p1, Point p2, Point p3, Point n)
|
||||
{
|
||||
// see http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
|
||||
// for a (not very good) explanation of the math
|
||||
int Ax = p2.x - p1.x, Ay = p2.y - p1.y;
|
||||
float u = (p3.x - p1.x) * Ax + (p3.y - p1.y) * Ay;
|
||||
u /= (Ax * Ax + Ay * Ay);
|
||||
n.x = p1.x + Math.round(Ax * u);
|
||||
n.y = p1.y + Math.round(Ay * u);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the intersection of two lines. Either line may be
|
||||
* considered as a line segment, and the intersecting point
|
||||
* is only considered valid if it lies upon the segment.
|
||||
* Note that Point extends Point2D.
|
||||
*
|
||||
* @param p1 and p2 the coordinates of the first line.
|
||||
* @param seg1 if the first line should be considered a segment.
|
||||
* @param p3 and p4 the coordinates of the second line.
|
||||
* @param seg2 if the second line should be considered a segment.
|
||||
* @param result the point that will be filled in with the intersecting
|
||||
* point.
|
||||
*
|
||||
* @return true if result was filled in, or false if the lines
|
||||
* are parallel or the point of intersection lies outside of a
|
||||
* segment.
|
||||
*/
|
||||
public static boolean lineIntersection (
|
||||
Point2D p1, Point2D p2, boolean seg1,
|
||||
Point2D p3, Point2D p4, boolean seg2, Point2D result)
|
||||
{
|
||||
// see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
|
||||
double y43 = p4.getY() - p3.getY();
|
||||
double x21 = p2.getX() - p1.getX();
|
||||
double x43 = p4.getX() - p3.getX();
|
||||
double y21 = p2.getY() - p1.getY();
|
||||
double denom = y43 * x21 - x43 * y21;
|
||||
if (denom == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double y13 = p1.getY() - p3.getY();
|
||||
double x13 = p1.getX() - p3.getX();
|
||||
double ua = (x43 * y13 - y43 * x13) / denom;
|
||||
if (seg1 && ((ua < 0) || (ua > 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seg2) {
|
||||
double ub = (x21 * y13 - y21 * x13) / denom;
|
||||
if ((ub < 0) || (ub > 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double x = p1.getX() + ua * x21;
|
||||
double y = p1.getY() + ua * y21;
|
||||
result.setLocation(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns less than zero if <code>p2</code> is on the left hand side
|
||||
* of the line created by <code>p1</code> and <code>theta</code> and
|
||||
* greater than zero if it is on the right hand side. In theory, it
|
||||
* will return zero if the point is on the line, but due to rounding
|
||||
* errors it almost always decides that it's not exactly on the line.
|
||||
*
|
||||
* @param p1 the point on the line whose side we're checking.
|
||||
* @param theta the (logical) angle defining the line.
|
||||
* @param p2 the point that lies on one side or the other of the line.
|
||||
*/
|
||||
public static int whichSide (Point p1, double theta, Point p2)
|
||||
{
|
||||
// obtain the point defining the right hand normal (N)
|
||||
theta += Math.PI/2;
|
||||
int x = p1.x + (int)Math.round(1000*Math.cos(theta)),
|
||||
y = p1.y + (int)Math.round(1000*Math.sin(theta));
|
||||
|
||||
// now dot the vector from p1->p2 with the vector from p1->N, if
|
||||
// it's positive, we're on the right hand side, if it's negative
|
||||
// we're on the left hand side and if it's zero, we're on the line
|
||||
return dot(p1.x, p1.y, p2.x, p2.y, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the position of the <code>tainer</code> rectangle to ensure
|
||||
* that it contains the <code>tained</code> rectangle. The
|
||||
* <code>tainer</code> rectangle must be larger than or equal to the
|
||||
* size of the <code>tained</code> rectangle.
|
||||
*/
|
||||
public static void shiftToContain (Rectangle tainer, Rectangle tained)
|
||||
{
|
||||
if (tained.x < tainer.x) {
|
||||
tainer.x = tained.x;
|
||||
}
|
||||
if (tained.y < tainer.y) {
|
||||
tainer.y = tained.y;
|
||||
}
|
||||
if (tained.x + tained.width > tainer.x + tainer.width) {
|
||||
tainer.x = tained.x - (tainer.width - tained.width);
|
||||
}
|
||||
if (tained.y + tained.height > tainer.y + tainer.height) {
|
||||
tainer.y = tained.y - (tainer.height - tained.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the target rectangle to the bounds of the source rectangle. If
|
||||
* the source rectangle is null, a new rectangle is created that is the
|
||||
* size of the target rectangle.
|
||||
*
|
||||
* @return the source rectangle.
|
||||
*/
|
||||
public static Rectangle grow (Rectangle source, Rectangle target)
|
||||
{
|
||||
if (target == null) {
|
||||
Log.warning("Can't grow with null rectangle [src=" + source +
|
||||
", tgt=" + target + "].");
|
||||
Thread.dumpStack();
|
||||
} else if (source == null) {
|
||||
source = new Rectangle(target);
|
||||
} else {
|
||||
source.add(target);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rectangle containing the specified tile in the supplied
|
||||
* larger rectangle. Tiles go from left to right, top to bottom.
|
||||
*/
|
||||
public static Rectangle getTile (
|
||||
int width, int height, int tileWidth, int tileHeight, int tileIndex)
|
||||
{
|
||||
// figure out from whence to crop the tile
|
||||
int tilesPerRow = width / tileWidth;
|
||||
|
||||
// if we got a bogus region, return bogus tile bounds
|
||||
if (tilesPerRow == 0) {
|
||||
return new Rectangle(0, 0, width, height);
|
||||
}
|
||||
|
||||
int row = tileIndex / tilesPerRow;
|
||||
int col = tileIndex % tilesPerRow;
|
||||
|
||||
// crop the tile-sized image chunk from the full image
|
||||
return new Rectangle(
|
||||
tileWidth*col, tileHeight*row, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.geom;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* this package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static final String PACKAGE = "geom";
|
||||
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log(PACKAGE);
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
|
||||
public static int getLevel ()
|
||||
{
|
||||
return com.samskivert.util.Log.getLevel(PACKAGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.samskivert.util.Queue;
|
||||
import com.samskivert.util.RunQueue;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.jme.renderer.Camera;
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
import com.jme.renderer.Renderer;
|
||||
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.state.LightState;
|
||||
import com.jme.scene.state.ZBufferState;
|
||||
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.system.JmeException;
|
||||
import com.jme.system.PropertiesIO;
|
||||
import com.jme.system.lwjgl.LWJGLPropertiesDialog;
|
||||
|
||||
import com.jme.input.InputHandler;
|
||||
import com.jme.input.KeyInput;
|
||||
import com.jme.input.Mouse;
|
||||
import com.jme.input.MouseInput;
|
||||
import com.jmex.bui.BRootNode;
|
||||
import com.jmex.bui.PolledRootNode;
|
||||
|
||||
import com.jme.light.PointLight;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.util.Timer;
|
||||
|
||||
import com.threerings.jme.camera.CameraHandler;
|
||||
|
||||
/**
|
||||
* Defines a basic application framework providing integration with the
|
||||
* <a href="../presents/package.html">Presents</a> networking system and
|
||||
* targeting the framerate of the display.
|
||||
*/
|
||||
public class JmeApp
|
||||
implements RunQueue
|
||||
{
|
||||
/**
|
||||
* Returns a context implementation that provides access to all the
|
||||
* necessary bits.
|
||||
*/
|
||||
public JmeContext getContext ()
|
||||
{
|
||||
return _ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the main initialization of the application. This method should
|
||||
* be called first, and then the {@link #run} method should be called
|
||||
* to begin the rendering/event loop. Derived classes can override
|
||||
* this, being sure to call super before doing their own
|
||||
* initalization.
|
||||
*
|
||||
* @return true if the application initialized successfully, false if
|
||||
* initialization failed. (See {@link #reportInitFailure}.)
|
||||
*/
|
||||
public boolean init ()
|
||||
{
|
||||
try {
|
||||
// initialize the rendering system
|
||||
initDisplay();
|
||||
if (!_display.isCreated()) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to initialize display?");
|
||||
}
|
||||
|
||||
// create an appropriate timer
|
||||
_timer = Timer.getTimer();
|
||||
|
||||
// start with the target FPS equal to the refresh rate (but
|
||||
// sometimes the refresh rate is reported as zero so don't let that
|
||||
// freak us out)
|
||||
setTargetFPS(Math.max(_display.getFrequency(), 60));
|
||||
|
||||
// initialize our main camera controls and user input handling
|
||||
initInput();
|
||||
|
||||
// initialize the root node
|
||||
initRoot();
|
||||
|
||||
// initialize the lighting
|
||||
initLighting();
|
||||
|
||||
// initialize the UI support stuff
|
||||
initInterface();
|
||||
|
||||
// update everything for the zeroth tick
|
||||
_iface.updateRenderState();
|
||||
_geom.updateRenderState();
|
||||
_root.updateGeometricState(0f, true);
|
||||
_root.updateRenderState();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Throwable t) {
|
||||
reportInitFailure(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether or not we display FPS and other statistics atop the
|
||||
* display.
|
||||
*/
|
||||
public void displayStatistics (boolean display)
|
||||
{
|
||||
if (display && (_stats == null)) {
|
||||
_stats = new StatsDisplay(_display.getRenderer());
|
||||
_stats.updateGeometricState(0f, true);
|
||||
_stats.updateRenderState();
|
||||
} else if (!display && (_stats != null)) {
|
||||
_stats = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are displaying statistics, false if not.
|
||||
*/
|
||||
public boolean showingStatistics ()
|
||||
{
|
||||
return (_stats != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target frames per second.
|
||||
*
|
||||
* @return the old target frames per second.
|
||||
*/
|
||||
public int setTargetFPS (int targetFPS)
|
||||
{
|
||||
int oldTargetFPS = _targetFPS;
|
||||
_targetFPS = targetFPS;
|
||||
_ticksPerFrame = _timer.getResolution() / _targetFPS;
|
||||
return oldTargetFPS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the frames per second averaged over the last 32 frames.
|
||||
*/
|
||||
public float getRecentFrameRate ()
|
||||
{
|
||||
return _timer.getFrameRate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the update and render part of the
|
||||
* update/render/process events application loop.
|
||||
*/
|
||||
public void setEnabled (boolean update, boolean render)
|
||||
{
|
||||
_updateEnabled = update;
|
||||
_renderEnabled = render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the main rendering and event processing loop. This method
|
||||
* will not return until the application is terminated with a call to
|
||||
* {@link #stop}.
|
||||
*/
|
||||
public void run ()
|
||||
{
|
||||
synchronized (this) {
|
||||
_dispatchThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
// enter the main rendering and event processing loop
|
||||
while (!_finished && !_display.isClosing()) {
|
||||
try {
|
||||
// render the current frame
|
||||
long frameStart = processFrame();
|
||||
|
||||
// and process events until it's time to render the next
|
||||
PROCESS_EVENTS:
|
||||
do {
|
||||
Runnable r = (Runnable)_evqueue.getNonBlocking();
|
||||
if (r != null) {
|
||||
r.run();
|
||||
}
|
||||
if (_timer.getTime() - frameStart >= _ticksPerFrame) {
|
||||
break PROCESS_EVENTS;
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
} while (!_finished);
|
||||
_failures = 0;
|
||||
|
||||
} catch (Throwable t) {
|
||||
Log.logStackTrace(t);
|
||||
// stick a fork in things if we fail too many times in a row
|
||||
if (++_failures > MAX_SUCCESSIVE_FAILURES) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
cleanup();
|
||||
} catch (Throwable t) {
|
||||
Log.logStackTrace(t);
|
||||
} finally {
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration (in seconds) between the previous frame and the
|
||||
* current frame.
|
||||
*/
|
||||
public float getFrameTime ()
|
||||
{
|
||||
return _frameTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the application to stop the main loop, cleanup and exit.
|
||||
*/
|
||||
public void stop ()
|
||||
{
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
// documentation inherited from interface RunQueue
|
||||
public void postRunnable (Runnable r)
|
||||
{
|
||||
_evqueue.append(r);
|
||||
}
|
||||
|
||||
// documentation inherited from interface RunQueue
|
||||
public boolean isDispatchThread ()
|
||||
{
|
||||
return Thread.currentThread() == _dispatchThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the display configuration and creates the display. This must
|
||||
* fill in {@link #_api} as a side-effect.
|
||||
*/
|
||||
protected DisplaySystem createDisplay ()
|
||||
{
|
||||
PropertiesIO props = new PropertiesIO(getConfigPath("jme.cfg"));
|
||||
if (!props.load()) {
|
||||
LWJGLPropertiesDialog dialog =
|
||||
new LWJGLPropertiesDialog(props, (String)null);
|
||||
while (dialog.isVisible()) {
|
||||
try {
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
Log.warning("Error waiting for dialog system, " +
|
||||
"using defaults.");
|
||||
}
|
||||
}
|
||||
}
|
||||
_api = props.getRenderer();
|
||||
DisplaySystem display = DisplaySystem.getDisplaySystem(_api);
|
||||
display.createWindow(props.getWidth(), props.getHeight(),
|
||||
props.getDepth(), props.getFreq(),
|
||||
props.getFullscreen());
|
||||
return display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the underlying rendering system, creating a display of
|
||||
* the proper resolution and depth.
|
||||
*/
|
||||
protected void initDisplay ()
|
||||
throws JmeException
|
||||
{
|
||||
// create the main display system
|
||||
_display = createDisplay();
|
||||
|
||||
// create a camera
|
||||
int width = _display.getWidth(), height = _display.getHeight();
|
||||
_camera = _display.getRenderer().createCamera(width, height);
|
||||
|
||||
// start with a black background
|
||||
_display.getRenderer().setBackgroundColor(ColorRGBA.black);
|
||||
|
||||
// set up the camera
|
||||
_camera.setFrustumPerspective(45.0f, width/(float)height, 1, 10000);
|
||||
Vector3f loc = new Vector3f(0.0f, 0.0f, 25.0f);
|
||||
Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
|
||||
Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
|
||||
Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);
|
||||
_camera.setFrame(loc, left, up, dir);
|
||||
_camera.update();
|
||||
_display.getRenderer().setCamera(_camera);
|
||||
|
||||
// tell the renderer to keep track of rendering information (total
|
||||
// triangles drawn, etc.)
|
||||
_display.getRenderer().enableStatistics(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a main input controller to handle the camera and deal with
|
||||
* global user input.
|
||||
*/
|
||||
protected void initInput ()
|
||||
{
|
||||
_camhand = createCameraHandler(_camera);
|
||||
_input = createInputHandler(_camhand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the camera handler which provides various camera manipulation
|
||||
* functionality.
|
||||
*/
|
||||
protected CameraHandler createCameraHandler (Camera camera)
|
||||
{
|
||||
return new CameraHandler(camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the input handler used to control our camera and manage non-UI
|
||||
* keyboard input.
|
||||
*/
|
||||
protected InputHandler createInputHandler (CameraHandler hand)
|
||||
{
|
||||
return new InputHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates our root node and sets up the basic rendering system.
|
||||
*/
|
||||
protected void initRoot ()
|
||||
{
|
||||
_root = new Node("Root");
|
||||
|
||||
// set up a node for our geometry
|
||||
_geom = new Node("Geometry");
|
||||
|
||||
// make everything opaque by default
|
||||
_geom.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
|
||||
|
||||
// set up a zbuffer
|
||||
ZBufferState zbuf = _display.getRenderer().createZBufferState();
|
||||
zbuf.setEnabled(true);
|
||||
zbuf.setFunction(ZBufferState.CF_LEQUAL);
|
||||
_geom.setRenderState(zbuf);
|
||||
_root.attachChild(_geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up some default lighting.
|
||||
*/
|
||||
protected void initLighting ()
|
||||
{
|
||||
PointLight light = new PointLight();
|
||||
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
light.setLocation(new Vector3f(100, 100, 100));
|
||||
light.setEnabled(true);
|
||||
|
||||
_lights = _display.getRenderer().createLightState();
|
||||
_lights.setEnabled(true);
|
||||
_lights.attach(light);
|
||||
_geom.setRenderState(_lights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes our user interface bits.
|
||||
*/
|
||||
protected void initInterface ()
|
||||
{
|
||||
// set up a node for our interface
|
||||
_iface = new Node("Interface");
|
||||
_root.attachChild(_iface);
|
||||
|
||||
// create our root node
|
||||
_rnode = createRootNode();
|
||||
_iface.attachChild(_rnode);
|
||||
|
||||
// we don't hide the cursor
|
||||
MouseInput.get().setCursorVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a customized root node to be created.
|
||||
*/
|
||||
protected BRootNode createRootNode ()
|
||||
{
|
||||
return new PolledRootNode(_timer, _input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when initialization fails to give the application a chance
|
||||
* to report the failure to the user.
|
||||
*/
|
||||
protected void reportInitFailure (Throwable t)
|
||||
{
|
||||
Log.logStackTrace(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single frame.
|
||||
*/
|
||||
protected final long processFrame ()
|
||||
{
|
||||
// update our simulation and render a frame
|
||||
long frameStart = _timer.getTime();
|
||||
if (_updateEnabled) {
|
||||
update(frameStart);
|
||||
}
|
||||
if (_renderEnabled) {
|
||||
render(frameStart);
|
||||
_display.getRenderer().displayBackBuffer();
|
||||
}
|
||||
return frameStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every frame to update whatever sort of real time business we
|
||||
* have that needs updating.
|
||||
*/
|
||||
protected void update (long frameTick)
|
||||
{
|
||||
// recalculate the frame rate
|
||||
_timer.update();
|
||||
|
||||
// update the camera handler
|
||||
_camhand.update(_frameTime);
|
||||
|
||||
// run all of the controllers attached to nodes
|
||||
_frameTime = (_lastTick == 0L) ? 0f : (float)(frameTick - _lastTick) /
|
||||
_timer.getResolution();
|
||||
_lastTick = frameTick;
|
||||
_root.updateGeometricState(_frameTime, true);
|
||||
|
||||
// update our stats display if we have one
|
||||
if (_stats != null) {
|
||||
_stats.update(_timer, _display.getRenderer());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every frame to issue the rendering instructions for this frame.
|
||||
*/
|
||||
protected void render (float frameTime)
|
||||
{
|
||||
// clear out our previous information
|
||||
_display.getRenderer().clearStatistics();
|
||||
_display.getRenderer().clearBuffers();
|
||||
|
||||
// draw the root node and all of its children
|
||||
_display.getRenderer().draw(_root);
|
||||
|
||||
// this would render bounding boxes
|
||||
// _display.getRenderer().drawBounds(_root);
|
||||
|
||||
// draw our stats atop everything
|
||||
if (_stats != null) {
|
||||
_display.getRenderer().draw(_stats);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the application is terminating cleanly after having
|
||||
* successfully completed initialization and begun the main loop.
|
||||
*/
|
||||
protected void cleanup ()
|
||||
{
|
||||
_display.reset();
|
||||
KeyInput.destroyIfInitalized();
|
||||
MouseInput.destroyIfInitalized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the display and exits the JVM process.
|
||||
*/
|
||||
protected void exit ()
|
||||
{
|
||||
if (_display != null) {
|
||||
_display.close();
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the necessary bits onto the supplied path to properly
|
||||
* locate it in our configuration directory.
|
||||
*/
|
||||
protected String getConfigPath (String file)
|
||||
{
|
||||
String cfgdir = ".narya";
|
||||
String home = System.getProperty("user.home");
|
||||
if (!StringUtil.isBlank(home)) {
|
||||
cfgdir = home + File.separator + cfgdir;
|
||||
}
|
||||
// create the configuration directory if it does not already exist
|
||||
File dir = new File(cfgdir);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir();
|
||||
}
|
||||
return cfgdir + File.separator + file;
|
||||
}
|
||||
|
||||
/** Provides access to various needed bits. */
|
||||
protected JmeContext _ctx = new JmeContext() {
|
||||
public DisplaySystem getDisplay () {
|
||||
return _display;
|
||||
}
|
||||
|
||||
public Renderer getRenderer () {
|
||||
return _display.getRenderer();
|
||||
}
|
||||
|
||||
public CameraHandler getCameraHandler () {
|
||||
return _camhand;
|
||||
}
|
||||
|
||||
public Node getGeometry () {
|
||||
return _geom;
|
||||
}
|
||||
|
||||
public Node getInterface () {
|
||||
return _iface;
|
||||
}
|
||||
|
||||
public InputHandler getInputHandler () {
|
||||
return _input;
|
||||
}
|
||||
|
||||
public BRootNode getRootNode () {
|
||||
return _rnode;
|
||||
}
|
||||
};
|
||||
|
||||
protected Timer _timer;
|
||||
protected Thread _dispatchThread;
|
||||
protected Queue _evqueue = new Queue();
|
||||
protected long _lastTick;
|
||||
protected float _frameTime;
|
||||
|
||||
protected String _api;
|
||||
protected DisplaySystem _display;
|
||||
protected Camera _camera;
|
||||
protected CameraHandler _camhand;
|
||||
|
||||
protected InputHandler _input;
|
||||
protected BRootNode _rnode;
|
||||
|
||||
protected long _ticksPerFrame;
|
||||
protected int _targetFPS;
|
||||
protected boolean _updateEnabled = true, _renderEnabled = true;
|
||||
protected boolean _finished;
|
||||
protected int _failures;
|
||||
|
||||
protected Node _root, _geom, _iface;
|
||||
protected LightState _lights;
|
||||
protected StatsDisplay _stats;
|
||||
|
||||
/** If we fail 100 frames in a row, stick a fork in ourselves. */
|
||||
protected static final int MAX_SUCCESSIVE_FAILURES = 100;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
|
||||
import org.lwjgl.LWJGLException;
|
||||
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.renderer.lwjgl.LWJGLRenderer;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jmex.awt.JMECanvas;
|
||||
import com.jmex.awt.JMECanvasImplementor;
|
||||
import com.jmex.awt.lwjgl.LWJGLCanvas;
|
||||
|
||||
import com.jmex.bui.CanvasRootNode;
|
||||
|
||||
/**
|
||||
* Extends the basic {@link JmeApp} with the necessary wiring to use the
|
||||
* GL/AWT bridge to display our GL interface in an AWT component.
|
||||
*/
|
||||
public class JmeCanvasApp extends JmeApp
|
||||
{
|
||||
public JmeCanvasApp (int width, int height)
|
||||
{
|
||||
_display = DisplaySystem.getDisplaySystem("LWJGL");
|
||||
|
||||
// create a throwaway canvas so that the display system knows it's
|
||||
// created, then create our custom canvas
|
||||
_display.createCanvas(width, height);
|
||||
try {
|
||||
_canvas = createCanvas();
|
||||
} catch (LWJGLException e) {
|
||||
Log.warning("Failed to create LWJGL canvas [error=" + e + "].");
|
||||
}
|
||||
((JMECanvas)_canvas).setImplementor(_winimp);
|
||||
_canvas.setBounds(0, 0, width, height);
|
||||
_canvas.addComponentListener(new ComponentAdapter() {
|
||||
public void componentResized (ComponentEvent ce) {
|
||||
_winimp.resizeCanvas(_canvas.getWidth(), _canvas.getHeight());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AWT canvas that contains our GL display.
|
||||
*/
|
||||
public Canvas getCanvas ()
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
public void run ()
|
||||
{
|
||||
Thread t = new Thread() {
|
||||
public void run () {
|
||||
while (!_finished) {
|
||||
// queue up another repaint
|
||||
_canvas.repaint();
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
// documentation inherited from interface RunQueue
|
||||
public void postRunnable (Runnable r)
|
||||
{
|
||||
EventQueue.invokeLater(r);
|
||||
}
|
||||
|
||||
// documentation inherited from interface RunQueue
|
||||
public boolean isDispatchThread ()
|
||||
{
|
||||
return EventQueue.isDispatchThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the LWJGL canvas instance.
|
||||
*/
|
||||
protected Canvas createCanvas ()
|
||||
throws LWJGLException
|
||||
{
|
||||
// LWJGL's canvas releases the context after painting. we make it
|
||||
// current again, because we want it valid when we process events.
|
||||
return new LWJGLCanvas() {
|
||||
public void update (Graphics g) {
|
||||
super.update(g);
|
||||
try {
|
||||
makeCurrent();
|
||||
} catch (LWJGLException e) {
|
||||
Log.warning("Failed to make context current [error=" +
|
||||
e + "].");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected DisplaySystem createDisplay ()
|
||||
{
|
||||
// we've already created our display earlier so just return it
|
||||
_api = "LWJGL";
|
||||
return _display;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void initInterface ()
|
||||
{
|
||||
_iface = new Node("Interface");
|
||||
_root.attachChild(_iface);
|
||||
|
||||
_rnode = new CanvasRootNode(_canvas);
|
||||
_iface.attachChild(_rnode);
|
||||
}
|
||||
|
||||
/** This is used if we embed our GL display in an AWT component. */
|
||||
protected JMECanvasImplementor _winimp = new JMECanvasImplementor() {
|
||||
public void doSetup () {
|
||||
super.doSetup();
|
||||
|
||||
LWJGLRenderer renderer =
|
||||
new LWJGLRenderer(_canvas.getWidth(), _canvas.getHeight());
|
||||
renderer.setHeadless(true);
|
||||
setRenderer(renderer);
|
||||
_display.setRenderer(renderer);
|
||||
DisplaySystem.updateStates(renderer);
|
||||
|
||||
if (!init()) {
|
||||
Log.warning("JmeCanvasApp init failed.");
|
||||
}
|
||||
}
|
||||
|
||||
public void doUpdate () {
|
||||
}
|
||||
|
||||
public void doRender () {
|
||||
// here we do our normal frame processing
|
||||
try {
|
||||
processFrame();
|
||||
// we don't process events as the AWT queue handles them
|
||||
_failures = 0;
|
||||
} catch (Throwable t) {
|
||||
Log.logStackTrace(t);
|
||||
// stick a fork in things if we fail too many
|
||||
// times in a row
|
||||
if (++_failures > MAX_SUCCESSIVE_FAILURES) {
|
||||
JmeCanvasApp.this.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected Canvas _canvas;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
import com.jme.input.InputHandler;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.system.DisplaySystem;
|
||||
|
||||
import com.jmex.bui.BRootNode;
|
||||
|
||||
import com.threerings.jme.camera.CameraHandler;
|
||||
|
||||
/**
|
||||
* Provides access to the various bits needed by things that operate in
|
||||
* JME land.
|
||||
*/
|
||||
public interface JmeContext
|
||||
{
|
||||
/** Returns the display to which we are rendering. */
|
||||
public DisplaySystem getDisplay ();
|
||||
|
||||
/** Returns the renderer being used to draw everything. */
|
||||
public Renderer getRenderer ();
|
||||
|
||||
/** Returns the handler for the camera being used to view the scene. */
|
||||
public CameraHandler getCameraHandler ();
|
||||
|
||||
/** Returns the main geometry of our scene graph. */
|
||||
public Node getGeometry ();
|
||||
|
||||
/** Returns the main interface node of our scene graph. */
|
||||
public Node getInterface ();
|
||||
|
||||
/** Returns our main input handler. */
|
||||
public InputHandler getInputHandler ();
|
||||
|
||||
/** Returns our UI root node. */
|
||||
public BRootNode getRootNode ();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 3099 2004-08-27 02:21:06Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* this package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("narya.jme");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Text;
|
||||
import com.jme.scene.state.AlphaState;
|
||||
import com.jme.scene.state.TextureState;
|
||||
import com.jme.util.TextureManager;
|
||||
import com.jme.util.Timer;
|
||||
|
||||
/**
|
||||
* Tracks and displays render statistics.
|
||||
*/
|
||||
public class StatsDisplay extends Node
|
||||
{
|
||||
public StatsDisplay (Renderer renderer)
|
||||
{
|
||||
super("StatsNode");
|
||||
|
||||
// create an alpha state that will allow us to blend the text on
|
||||
// top of whatever else is below
|
||||
AlphaState astate = renderer.createAlphaState();
|
||||
astate.setBlendEnabled(true);
|
||||
astate.setSrcFunction(AlphaState.SB_SRC_ALPHA);
|
||||
astate.setDstFunction(AlphaState.DB_ONE);
|
||||
astate.setTestEnabled(true);
|
||||
astate.setTestFunction(AlphaState.TF_GREATER);
|
||||
astate.setEnabled(true);
|
||||
|
||||
// create a font texture
|
||||
TextureState font = renderer.createTextureState();
|
||||
font.setTexture(
|
||||
TextureManager.loadTexture(
|
||||
getClass().getClassLoader().getResource(DEFAULT_JME_FONT),
|
||||
Texture.MM_LINEAR, Texture.FM_LINEAR));
|
||||
font.setEnabled(true);
|
||||
|
||||
_text = new Text("StatsLabel", "");
|
||||
_text.setCullMode(CULL_NEVER);
|
||||
_text.setTextureCombineMode(TextureState.REPLACE);
|
||||
|
||||
attachChild(_text);
|
||||
setRenderState(font);
|
||||
setRenderState(astate);
|
||||
}
|
||||
|
||||
public void update (Timer timer, Renderer renderer)
|
||||
{
|
||||
_stats.setLength(0);
|
||||
_stats.append("FPS: ").append((int)timer.getFrameRate());
|
||||
_stats.append(" - ").append(renderer.getStatistics(_temp));
|
||||
_text.print(_stats);
|
||||
}
|
||||
|
||||
protected Text _text;
|
||||
protected StringBuffer _stats = new StringBuffer();
|
||||
protected StringBuffer _temp = new StringBuffer();
|
||||
|
||||
protected static final String DEFAULT_JME_FONT =
|
||||
"rsrc/media/jme/defaultfont.tga";
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Plane;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
/**
|
||||
* Provides various useful mechanisms for manipulating the camera.
|
||||
*/
|
||||
public class CameraHandler
|
||||
{
|
||||
/**
|
||||
* Creates a new camera handler. The camera begins life at the origin,
|
||||
* facing in the negative z direction (pointing at the ground).
|
||||
*/
|
||||
public CameraHandler (Camera camera)
|
||||
{
|
||||
_camera = camera;
|
||||
resetAxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the camera orientation to its initial state.
|
||||
*/
|
||||
public void resetAxes ()
|
||||
{
|
||||
_camera.getDirection().set(0, 0, -1);
|
||||
_camera.getLeft().set(-1, 0, 0);
|
||||
_camera.getUp().set(0, 1, 0);
|
||||
_camera.update();
|
||||
|
||||
_rxdir.set(1, 0, 0);
|
||||
_rydir.set(0, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures limits on the camera tilt.
|
||||
*/
|
||||
public void setTiltLimits (float minAngle, float maxAngle)
|
||||
{
|
||||
_minTilt = minAngle;
|
||||
_maxTilt = maxAngle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures limits on the distance the camera can be panned.
|
||||
*
|
||||
* @param boundViaFrustum if true instead of bounding the camera's
|
||||
* position, we will compute the intersections of the view frustum with the
|
||||
* ground plan and bound that rectangle into the specified bounds.
|
||||
* <em>Note:</em> the camera must generally be pointing down at the ground
|
||||
* (up to perhaps 45 degrees or so) for this to work. At higher angles the
|
||||
* back of the view frustum will intersect the ground plane at or near
|
||||
* infinity.
|
||||
*/
|
||||
public void setPanLimits (float minX, float minY, float maxX, float maxY,
|
||||
boolean boundViaFrustum)
|
||||
{
|
||||
_minX = minX;
|
||||
_minY = minY;
|
||||
_maxX = maxX;
|
||||
_maxY = maxY;
|
||||
_boundViaFrustum = boundViaFrustum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the minimum and maximum zoom values allowed for the
|
||||
* camera.
|
||||
*/
|
||||
public void setZoomLimits (float minZoom, float maxZoom)
|
||||
{
|
||||
_minZoom = minZoom;
|
||||
_maxZoom = maxZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the current set of limits.
|
||||
*/
|
||||
public void setLimitsEnabled (boolean enabled)
|
||||
{
|
||||
_limitsEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the camera zoom level to a value between zero (zoomed in maximally)
|
||||
* and 1 (zoomed out maximally). Zoom limits must have already been set up
|
||||
* via a call to {@link #setZoomLimits}.
|
||||
*/
|
||||
public void setZoomLevel (float level)
|
||||
{
|
||||
// Log.info("Zoom " + level + " " + _camera.getLocation());
|
||||
level = Math.max(0f, Math.min(level, 1f));
|
||||
_camera.getLocation().z = _minZ + (_maxZ - _minZ) * level;
|
||||
_camera.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current camera zoom level.
|
||||
*/
|
||||
public float getZoomLevel ()
|
||||
{
|
||||
return (_camera.getLocation().z - _minZ) / (_maxZ - _minZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a camera path observer.
|
||||
*/
|
||||
public void addCameraObserver (CameraPath.Observer camobs)
|
||||
{
|
||||
_campathobs.add(camobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a camera path observer.
|
||||
*/
|
||||
public void removeCameraObserver (CameraPath.Observer camobs)
|
||||
{
|
||||
_campathobs.remove(camobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the camera moving along a path which will be updated every tick
|
||||
* until it is complete.
|
||||
*/
|
||||
public void moveCamera (CameraPath path)
|
||||
{
|
||||
if (_campath != null) {
|
||||
_campath.abort();
|
||||
_campathobs.apply(new CompletedOp(_campath));
|
||||
}
|
||||
_campath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the camera is currently animating along a path, false if
|
||||
* it is not.
|
||||
*/
|
||||
public boolean cameraIsMoving ()
|
||||
{
|
||||
return (_campath != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips immediately to the end of the current camera path.
|
||||
*/
|
||||
public void skipPath ()
|
||||
{
|
||||
// fake an update far enough into the future to trick the camera path
|
||||
// into thinking it's done
|
||||
update(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the {@link JmeApp} on every frame to allow the handler
|
||||
* to update the camera as necessary.
|
||||
*/
|
||||
public void update (float frameTime)
|
||||
{
|
||||
if (_campath != null) {
|
||||
if (_campath.tick(frameTime)) {
|
||||
CameraPath opath = _campath;
|
||||
_campath = null;
|
||||
_campathobs.apply(new CompletedOp(opath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the camera being manipulated by this handler.
|
||||
*/
|
||||
public Camera getCamera ()
|
||||
{
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the camera's location. The specified location will be bounded
|
||||
* within the current pan and zoom limits.
|
||||
*/
|
||||
public void setLocation (Vector3f location)
|
||||
{
|
||||
_camera.setLocation(bound(location));
|
||||
_camera.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pans the camera the specified distance in the x and y directions. These
|
||||
* distances will be multiplied by the current "view" x and y axes and
|
||||
* added to the camera's position.
|
||||
*/
|
||||
public void panCamera (float x, float y)
|
||||
{
|
||||
Vector3f loc = _camera.getLocation();
|
||||
loc.addLocal(_rxdir.mult(x, _temp));
|
||||
loc.addLocal(_rydir.mult(y, _temp));
|
||||
setLocation(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms the camera in (distance < 0) and out (distance > 0) by the
|
||||
* specified amount. The distance is multiplied by the camera's current
|
||||
* direction and added to its current position, which is then bounded into
|
||||
* the pan and zoom volume.
|
||||
*/
|
||||
public void zoomCamera (float distance)
|
||||
{
|
||||
Vector3f loc = _camera.getLocation();
|
||||
float dist = getGroundPoint().distance(loc),
|
||||
ndist = Math.min(Math.max(dist + distance, _minZoom), _maxZoom);
|
||||
if ((distance = ndist - dist) == 0f) {
|
||||
return;
|
||||
}
|
||||
loc.subtractLocal(_camera.getDirection().mult(distance, _temp));
|
||||
setLocation(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the point on the ground at which the camera is "looking" and
|
||||
* rotates the camera from that point around the specified vector by the
|
||||
* specified angle. Additionally zooms the camera in (deltaZoom < 0) or out
|
||||
* (deltaZoom > 0) along its direction of view by the specified amount.
|
||||
*/
|
||||
public void rotateCamera (
|
||||
Vector3f spot, Vector3f axis, float deltaAngle, float deltaZoom)
|
||||
{
|
||||
// get a vector from the camera's current position to the point around
|
||||
// which we're going to orbit
|
||||
Vector3f direction = _camera.getLocation().subtract(spot);
|
||||
|
||||
// if we're rotating around the left vector, impose tilt limits
|
||||
if (axis == _camera.getLeft()) {
|
||||
float angle = FastMath.asin(_ground.normal.dot(direction) /
|
||||
direction.length());
|
||||
float nangle = Math.min(Math.max(angle + deltaAngle, _minTilt),
|
||||
_maxTilt);
|
||||
if ((deltaAngle = nangle - angle) == 0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// create a rotation matrix
|
||||
_rotm.fromAxisAngle(axis, deltaAngle);
|
||||
|
||||
// rotate the direction vector and the camera itself
|
||||
_rotm.mult(direction, direction);
|
||||
_rotm.mult(_camera.getUp(), _camera.getUp());
|
||||
_rotm.mult(_camera.getLeft(), _camera.getLeft());
|
||||
_rotm.mult(_camera.getDirection(), _camera.getDirection());
|
||||
|
||||
// if we're rotating around the ground normal, we need to update our
|
||||
// notion of side-to-side and forward for panning
|
||||
if (axis == _ground.normal) {
|
||||
_rotm.mult(_rxdir, _rxdir);
|
||||
_rotm.mult(_rydir, _rydir);
|
||||
}
|
||||
|
||||
// finally move the camera to its new location, zooming in or out in
|
||||
// the process
|
||||
float scale = 1 + (deltaZoom / direction.length());
|
||||
direction.scaleAdd(scale, spot);
|
||||
setLocation(direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swings the camera perpendicular to the ground normal, around the point
|
||||
* on the ground at which it is looking.
|
||||
*/
|
||||
public void orbitCamera (float deltaAngle)
|
||||
{
|
||||
rotateCamera(getGroundPoint(), _ground.normal, deltaAngle, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swings the camera perpendicular to its left vector around the point on
|
||||
* the ground at which it is looking.
|
||||
*/
|
||||
public void tiltCamera (float deltaAngle)
|
||||
{
|
||||
rotateCamera(getGroundPoint(), _camera.getLeft(), deltaAngle, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point on the ground (z = 0) at which the camera is looking.
|
||||
*/
|
||||
public Vector3f getGroundPoint ()
|
||||
{
|
||||
float dist = -1f * _ground.normal.dot(_camera.getLocation()) /
|
||||
_ground.normal.dot(_camera.getDirection());
|
||||
return _camera.getLocation().add(_camera.getDirection().mult(dist));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ground normal. Z is the default ground plane and the normal
|
||||
* points in the positive z direction. <em>Do not modify this value.</em>
|
||||
*/
|
||||
public Vector3f getGroundNormal ()
|
||||
{
|
||||
return _ground.normal;
|
||||
}
|
||||
|
||||
protected Vector3f bound (Vector3f loc)
|
||||
{
|
||||
if (!_limitsEnabled) {
|
||||
return loc;
|
||||
}
|
||||
if (_boundViaFrustum) {
|
||||
bound(_camera.getFrustumLeft(), _camera.getFrustumTop(), loc);
|
||||
bound(_camera.getFrustumLeft(), _camera.getFrustumBottom(), loc);
|
||||
bound(_camera.getFrustumRight(), _camera.getFrustumTop(), loc);
|
||||
bound(_camera.getFrustumRight(), _camera.getFrustumBottom(), loc);
|
||||
} else {
|
||||
loc.x = Math.max(Math.min(loc.x, _maxX), _minX);
|
||||
loc.y = Math.max(Math.min(loc.y, _maxY), _minY);
|
||||
}
|
||||
loc.z = Math.max(Math.min(loc.z, _maxZ), _minZ);
|
||||
return loc;
|
||||
}
|
||||
|
||||
protected void bound (float left, float up, Vector3f loc)
|
||||
{
|
||||
// start with the location of the camera moved out into the near
|
||||
// frustum plane
|
||||
_temp.set(loc);
|
||||
_temp.scaleAdd(_camera.getFrustumNear(), _camera.getDirection(), loc);
|
||||
|
||||
// then slide it over to a corner of the near frustum rectangle
|
||||
_temp.scaleAdd(left, _camera.getLeft(), _temp);
|
||||
_temp.scaleAdd(up, _camera.getUp(), _temp);
|
||||
|
||||
// turn this into a vector with origin at the camera's location
|
||||
_temp.subtractLocal(loc);
|
||||
_temp.normalizeLocal();
|
||||
|
||||
// determine the intersection of said vector with the ground plane
|
||||
float dist = -1f * _ground.normal.dot(loc) / _ground.normal.dot(_temp);
|
||||
_temp.scaleAdd(dist, _temp, loc);
|
||||
|
||||
// we then assume that if the corner of the "viewable ground rectangle"
|
||||
// is outside our bounds that we can simply adjust the camera location
|
||||
// by the amount it is out of bounds
|
||||
if (_temp.x > _maxX) {
|
||||
loc.x -= (_temp.x - _maxX);
|
||||
} else if (_temp.x < _minX) {
|
||||
loc.x += (_minX - _temp.x);
|
||||
}
|
||||
if (_temp.y > _maxY) {
|
||||
loc.y -= (_temp.y - _maxY);
|
||||
} else if (_temp.y < _minY) {
|
||||
loc.y += (_minY - _temp.y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to dispatch {@link CameraPath.Observer#pathCompleted}. */
|
||||
protected static class CompletedOp implements ObserverList.ObserverOp
|
||||
{
|
||||
public CompletedOp (CameraPath path) {
|
||||
_path = path;
|
||||
}
|
||||
public boolean apply (Object observer) {
|
||||
return ((CameraPath.Observer)observer).pathCompleted(_path);
|
||||
}
|
||||
protected CameraPath _path;
|
||||
}
|
||||
|
||||
protected Camera _camera;
|
||||
protected CameraPath _campath;
|
||||
protected ObserverList _campathobs =
|
||||
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
|
||||
protected Matrix3f _rotm = new Matrix3f();
|
||||
protected Vector3f _temp = new Vector3f();
|
||||
|
||||
protected boolean _boundViaFrustum, _limitsEnabled = true;
|
||||
protected float _minX = -Float.MAX_VALUE, _maxX = Float.MAX_VALUE;
|
||||
protected float _minY = -Float.MAX_VALUE, _maxY = Float.MAX_VALUE;
|
||||
protected float _minZ = -Float.MAX_VALUE, _maxZ = Float.MAX_VALUE;
|
||||
protected float _minZoom = 0f, _maxZoom = Float.MAX_VALUE;
|
||||
protected float _minTilt = -Float.MAX_VALUE, _maxTilt = Float.MAX_VALUE;
|
||||
|
||||
protected Vector3f _rxdir = new Vector3f(1, 0, 0);
|
||||
protected Vector3f _rydir = new Vector3f(0, 1, 0);
|
||||
|
||||
protected static final Vector3f _xdir = new Vector3f(1, 0, 0);
|
||||
protected static final Vector3f _ydir = new Vector3f(0, 1, 0);
|
||||
|
||||
protected static final Plane _ground = new Plane(new Vector3f(0, 0, 1), 0);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
/**
|
||||
* Used to move the camera along a particular path.
|
||||
*/
|
||||
public abstract class CameraPath
|
||||
{
|
||||
/** Used to inform observers when a camera path is completed or aborted. */
|
||||
public interface Observer
|
||||
{
|
||||
/**
|
||||
* Called when this path is finished (potentially early because another
|
||||
* path was set before this path completed).
|
||||
*
|
||||
* @param path the path that was completed.
|
||||
*
|
||||
* @return true if the observer should remain in the list, false if it
|
||||
* should be removed following this notification.
|
||||
*/
|
||||
public boolean pathCompleted (CameraPath path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called on every frame to allow the path to adjust the position
|
||||
* of the camera.
|
||||
*
|
||||
* @return true if the path is completed and can be disposed, false if it
|
||||
* is not yet completed.
|
||||
*/
|
||||
public abstract boolean tick (float secondsSince);
|
||||
|
||||
/**
|
||||
* Called if this path is aborted prior to completion due to being replaced
|
||||
* by a new camera path.
|
||||
*/
|
||||
public void abort ()
|
||||
{
|
||||
}
|
||||
|
||||
protected CameraPath (CameraHandler camhand)
|
||||
{
|
||||
_camhand = camhand;
|
||||
}
|
||||
|
||||
protected CameraHandler _camhand;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.jme.input.InputHandler;
|
||||
import com.jme.input.KeyBindingManager;
|
||||
import com.jme.input.KeyInput;
|
||||
import com.jme.input.RelativeMouse;
|
||||
import com.jme.input.action.*;
|
||||
import com.jme.input.action.InputActionEvent;
|
||||
|
||||
import com.threerings.jme.JmeApp;
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Sets up camera controls for moving around from a top-down perspective,
|
||||
* suitable for strategy games and their ilk. The "ground" is assumed to be the
|
||||
* XY plane.
|
||||
*/
|
||||
public class GodViewHandler extends InputHandler
|
||||
{
|
||||
/**
|
||||
* Creates the handler.
|
||||
*
|
||||
* @param cam The camera to move with this handler.
|
||||
*/
|
||||
public GodViewHandler (CameraHandler camhand)
|
||||
{
|
||||
_camhand = camhand;
|
||||
setKeyBindings();
|
||||
addActions();
|
||||
}
|
||||
|
||||
protected void setKeyBindings ()
|
||||
{
|
||||
KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
|
||||
|
||||
// the key bindings for the pan actions
|
||||
keyboard.set("forward", KeyInput.KEY_W);
|
||||
keyboard.set("arrow_forward", KeyInput.KEY_UP);
|
||||
keyboard.set("backward", KeyInput.KEY_S);
|
||||
keyboard.set("arrow_backward", KeyInput.KEY_DOWN);
|
||||
keyboard.set("left", KeyInput.KEY_A);
|
||||
keyboard.set("arrow_left", KeyInput.KEY_LEFT);
|
||||
keyboard.set("right", KeyInput.KEY_D);
|
||||
keyboard.set("arrow_right", KeyInput.KEY_RIGHT);
|
||||
|
||||
// the key bindings for the zoom actions
|
||||
keyboard.set("zoomIn", KeyInput.KEY_UP);
|
||||
keyboard.set("zoomOut", KeyInput.KEY_DOWN);
|
||||
|
||||
// the key bindings for the orbit actions
|
||||
keyboard.set("turnRight", KeyInput.KEY_RIGHT);
|
||||
keyboard.set("turnLeft", KeyInput.KEY_LEFT);
|
||||
|
||||
// the key bindings for the tilt actions
|
||||
keyboard.set("tiltForward", KeyInput.KEY_HOME);
|
||||
keyboard.set("tiltBack", KeyInput.KEY_END);
|
||||
|
||||
keyboard.set("screenshot", KeyInput.KEY_F12);
|
||||
}
|
||||
|
||||
protected void addActions ()
|
||||
{
|
||||
addAction(new KeyScreenShotAction(), "screenshot", false);
|
||||
addPanActions();
|
||||
addZoomActions();
|
||||
addOrbitActions();
|
||||
addTiltActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for panning the camera around the scene.
|
||||
*/
|
||||
protected void addPanActions ()
|
||||
{
|
||||
InputAction forward = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(0, speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
forward.setSpeed(0.5f);
|
||||
addAction(forward, "forward", true);
|
||||
addAction(forward, "arrow_forward", true);
|
||||
|
||||
InputAction backward = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(0, -speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
backward.setSpeed(0.5f);
|
||||
addAction(backward, "backward", true);
|
||||
addAction(backward, "arrow_backward", true);
|
||||
|
||||
InputAction left = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(-speed * evt.getTime(), 0);
|
||||
}
|
||||
};
|
||||
left.setSpeed(0.5f);
|
||||
addAction(left, "left", true);
|
||||
addAction(left, "arrow_left", true);
|
||||
|
||||
InputAction right = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(speed * evt.getTime(), 0);
|
||||
}
|
||||
};
|
||||
right.setSpeed(0.5f);
|
||||
addAction(right, "right", true);
|
||||
addAction(right, "arrow_right", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for zooming the camaera in and out.
|
||||
*/
|
||||
protected void addZoomActions ()
|
||||
{
|
||||
InputAction zoomIn = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.zoomCamera(-speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
zoomIn.setSpeed(0.5f);
|
||||
addAction(zoomIn, "zoomIn", true);
|
||||
|
||||
InputAction zoomOut = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.zoomCamera(speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
zoomOut.setSpeed(0.5f);
|
||||
addAction(zoomOut, "zoomOut", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for orbiting the camera around the viewpoint.
|
||||
*/
|
||||
protected void addOrbitActions ()
|
||||
{
|
||||
addAction(new OrbitAction(-FastMath.PI / 2), "turnRight", true);
|
||||
addAction(new OrbitAction(FastMath.PI / 2), "turnLeft", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for tilting the camera (rotating around the yaw axis).
|
||||
*/
|
||||
protected void addTiltActions ()
|
||||
{
|
||||
addAction(new TiltAction(-FastMath.PI / 2), "tiltForward", true);
|
||||
addAction(new TiltAction(FastMath.PI / 2), "tiltBack", true);
|
||||
}
|
||||
|
||||
protected class OrbitAction extends InputAction
|
||||
{
|
||||
public OrbitAction (float radPerSec)
|
||||
{
|
||||
_radPerSec = radPerSec;
|
||||
}
|
||||
|
||||
public void performAction (InputActionEvent evt)
|
||||
{
|
||||
_camhand.orbitCamera(_radPerSec * evt.getTime());
|
||||
}
|
||||
|
||||
protected float _radPerSec;
|
||||
}
|
||||
|
||||
protected class TiltAction extends InputAction
|
||||
{
|
||||
public TiltAction (float radPerSec)
|
||||
{
|
||||
_radPerSec = radPerSec;
|
||||
}
|
||||
|
||||
public void performAction (InputActionEvent evt)
|
||||
{
|
||||
_camhand.tiltCamera(_radPerSec * evt.getTime());
|
||||
}
|
||||
|
||||
protected float _radPerSec;
|
||||
}
|
||||
|
||||
protected CameraHandler _camhand;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
/**
|
||||
* Pans the camera to the specified location in the specified amount of time.
|
||||
*/
|
||||
public class PanPath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a panning path for the specified camera that will retain the
|
||||
* camera's current orientation.
|
||||
*
|
||||
* @param target the target position for the camera.
|
||||
* @param duration the number of seconds in which to pan the camera.
|
||||
*/
|
||||
public PanPath (CameraHandler camhand, Vector3f target, float duration)
|
||||
{
|
||||
this(camhand, target, null, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panning path for the specified camera.
|
||||
*
|
||||
* @param target the target position for the camera.
|
||||
* @param trot the target rotation for the camera (or <code>null</code> to
|
||||
* keep its current orientation)
|
||||
* @param duration the number of seconds in which to pan the camera.
|
||||
*/
|
||||
public PanPath (CameraHandler camhand, Vector3f target, Quaternion trot,
|
||||
float duration)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
Camera cam = camhand.getCamera();
|
||||
_start = new Vector3f(cam.getLocation());
|
||||
_velocity = target.subtract(_start);
|
||||
_velocity.divideLocal(duration);
|
||||
if (trot != null) {
|
||||
_irot = new Quaternion();
|
||||
_irot.fromAxes(cam.getLeft(), cam.getUp(), cam.getDirection());
|
||||
_trot = trot;
|
||||
}
|
||||
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
||||
_camloc.scaleAdd(_elapsed, _velocity, _start);
|
||||
_camhand.setLocation(_camloc);
|
||||
if (_irot != null) {
|
||||
_camhand.getCamera().setAxes(_axes.slerp(_irot, _trot,
|
||||
_elapsed / _duration));
|
||||
}
|
||||
return (_elapsed >= _duration);
|
||||
}
|
||||
|
||||
protected Vector3f _start, _velocity, _camloc = new Vector3f();
|
||||
protected Quaternion _irot, _trot, _axes = new Quaternion();
|
||||
protected float _elapsed, _duration;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Moves the camera along a cubic Hermite spline path defined by the start and
|
||||
* end locations and directions. Spline formulas obtained from
|
||||
* <a href="http://en.wikipedia.org/wiki/Cubic_Hermite_spline">Wikipedia</a>.
|
||||
*/
|
||||
public class SplinePath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a cubic spline path for the camera to follow.
|
||||
*
|
||||
* @param tloc the target location
|
||||
* @param tdir the target direction
|
||||
* @param axis the heading axis (typically {@link Vector3f#UNIT_Z})
|
||||
* @param duration the duration of the path
|
||||
* @param tension the tension parameter, which can range from zero to one:
|
||||
* higher tension values create a more direct path with a sharper turn,
|
||||
* lower values create a rounder path with a smoother turn
|
||||
*/
|
||||
public SplinePath (CameraHandler camhand, Vector3f tloc,
|
||||
Vector3f tdir, Vector3f axis, float duration, float tension)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
// get the spline function coefficients
|
||||
Camera cam = camhand.getCamera();
|
||||
_p0 = new Vector3f(cam.getLocation());
|
||||
_p1 = new Vector3f(tloc);
|
||||
float tscale = (1f - tension) * _p0.distance(_p1);
|
||||
_m0 = cam.getDirection().mult(tscale);
|
||||
_m1 = tdir.mult(tscale);
|
||||
|
||||
_axis = axis;
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
||||
float t = _elapsed / _duration, t2 = t*t, t3 = t2*t,
|
||||
h00 = 2*t3 - 3*t2 + 1,
|
||||
h00p = 6*t2 - 6*t,
|
||||
h10 = t3 - 2*t2 + t,
|
||||
h10p = 3*t2 - 4*t + 1,
|
||||
h01 = -2*t3 + 3*t2,
|
||||
h01p = -6*t2 + 6*t,
|
||||
h11 = t3 - t2,
|
||||
h11p = 3*t2 - 2*t;
|
||||
|
||||
// take the derivative to find the direction
|
||||
_p0.mult(h00p, _dir);
|
||||
_dir.scaleAdd(h10p, _m0, _dir);
|
||||
_dir.scaleAdd(h01p, _p1, _dir);
|
||||
_dir.scaleAdd(h11p, _m1, _dir);
|
||||
_dir.normalizeLocal();
|
||||
|
||||
// evaluate the spline function to find the location
|
||||
_p0.mult(h00, _loc);
|
||||
_loc.scaleAdd(h10, _m0, _loc);
|
||||
_loc.scaleAdd(h01, _p1, _loc);
|
||||
_loc.scaleAdd(h11, _m1, _loc);
|
||||
|
||||
// compute the left and up vectors using the direction and
|
||||
// axis vectors
|
||||
Camera cam = _camhand.getCamera();
|
||||
_axis.cross(_dir, _left);
|
||||
_left.normalizeLocal();
|
||||
_dir.cross(_left, _up);
|
||||
|
||||
// update the camera's location and orientation
|
||||
cam.setFrame(_loc, _left, _up, _dir);
|
||||
|
||||
return _elapsed >= _duration;
|
||||
}
|
||||
|
||||
/** The parameters of the spline. */
|
||||
Vector3f _p0, _m0, _p1, _m1, _axis;
|
||||
|
||||
/** Working vectors. */
|
||||
Vector3f _loc = new Vector3f(), _left = new Vector3f(),
|
||||
_up = new Vector3f(), _dir = new Vector3f();
|
||||
|
||||
/** The elapsed time and total duration. */
|
||||
float _elapsed, _duration;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Swings the camera around a point of interest (which should be somewhere
|
||||
* along the camera's view vector). Also optionally pans the camera and/or
|
||||
* zooms it in or out (moves it along its view vector) in the process.
|
||||
*
|
||||
* <p align="center"><img src="rotate_zoom.png">
|
||||
*/
|
||||
public class SwingPath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a rotating, zooming path for the specified camera.
|
||||
*
|
||||
* @param spot the point of interest around which to swing the camera.
|
||||
* @param axis the axis around which to rotate the camera.
|
||||
* @param angle the angle through which to rotate the camera.
|
||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
||||
* the camera (in radians per second).
|
||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
||||
* = in, positive = out).
|
||||
*/
|
||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f axis,
|
||||
float angle, float angvel, float zoom)
|
||||
{
|
||||
this(camhand, spot, axis, angle, angvel, null, 0f, null, zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rotating, panning, zooming path for the specified camera.
|
||||
*
|
||||
* @param spot the point of interest around which to swing the camera.
|
||||
* @param paxis the primary axis around which to rotate the camera.
|
||||
* @param pangle the angle through which to rotate the camera about the
|
||||
* primary axis.
|
||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
||||
* the camera (in radians per second) about the primary angle.
|
||||
* @param saxis the secondary axis around which to rotate the camera, or
|
||||
* <code>null</code> for none
|
||||
* @param sangle the angle through which to rotate the camera about the
|
||||
* secondary axis
|
||||
* @param pan the amount to pan the camera, or <code>null</code> for none
|
||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
||||
* = in, positive = out).
|
||||
*/
|
||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f paxis,
|
||||
float pangle, float angvel, Vector3f saxis, float sangle,
|
||||
Vector3f pan, float zoom)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
if (pangle == 0) {
|
||||
Log.warning("Requested to swing camera through zero degrees " +
|
||||
"[spot=" + spot + ", paxis=" + paxis +
|
||||
", angvel=" + angvel + ", zoom=" + zoom + "].");
|
||||
pangle = 0.0001f;
|
||||
}
|
||||
if (angvel <= 0) {
|
||||
Log.warning("Requested to swing camera with invalid velocity " +
|
||||
"[spot=" + spot + ", paxis=" + paxis + ", pangle=" +
|
||||
pangle + ", angvel=" + angvel + ", zoom=" + zoom +
|
||||
"].");
|
||||
angvel = FastMath.PI;
|
||||
}
|
||||
|
||||
_spot = new Vector3f(spot);
|
||||
_paxis = paxis;
|
||||
_pangle = pangle;
|
||||
_pangvel = (pangle > 0) ? angvel : -1 * angvel;
|
||||
_saxis = (saxis == null) ? null : new Vector3f(saxis);
|
||||
_sangle = sangle;
|
||||
_sangvel = _sangle * _pangvel / _pangle;
|
||||
_pan = pan;
|
||||
if (_pan != null) {
|
||||
_panvel = _pan.mult(_pangvel / _pangle);
|
||||
_panned = new Vector3f();
|
||||
}
|
||||
_zoom = zoom;
|
||||
_zoomvel = _zoom * _pangvel / _pangle;
|
||||
|
||||
// Log.info("Swinging camera [angle=" + _angle + ", angvel=" + _angvel +
|
||||
// ", zoom=" + _zoom + ", zoomvel=" + _zoomvel + "].");
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
float deltaPAngle = (secondsSince * _pangvel);
|
||||
float deltaSAngle = (secondsSince * _sangvel);
|
||||
float deltaZoom = (secondsSince * _zoomvel);
|
||||
_protated += deltaPAngle;
|
||||
_srotated += deltaSAngle;
|
||||
_zoomed += deltaZoom;
|
||||
if (_pan != null) {
|
||||
_panvel.mult(secondsSince, _deltaPan);
|
||||
_panned.addLocal(_deltaPan);
|
||||
}
|
||||
|
||||
// clamp our rotation at the target angle and determine whether or not
|
||||
// we're done
|
||||
boolean done = false;
|
||||
if (_pangle > 0 && _protated > _pangle ||
|
||||
_pangle < 0 && _protated < _pangle) {
|
||||
deltaPAngle -= (_protated - _pangle);
|
||||
deltaSAngle -= (_srotated - _sangle);
|
||||
deltaZoom -= (_zoomed - _zoom);
|
||||
if (_pan != null) {
|
||||
_deltaPan.subtractLocal(_panned).addLocal(_pan);
|
||||
}
|
||||
_protated = _pangle;
|
||||
_srotated = _sangle;
|
||||
_panned = _pan;
|
||||
_zoomed = _zoom;
|
||||
done = true;
|
||||
}
|
||||
|
||||
// have the camera handler do the necessary rotating, panning, zooming
|
||||
if (_pan != null) {
|
||||
_camhand.setLocation(
|
||||
_camhand.getCamera().getLocation().addLocal(_deltaPan));
|
||||
_spot.addLocal(_deltaPan);
|
||||
}
|
||||
_camhand.rotateCamera(_spot, _paxis, deltaPAngle, deltaZoom);
|
||||
if (_saxis != null) {
|
||||
_rot.fromAngleAxis(deltaPAngle, _paxis).multLocal(_saxis);
|
||||
_camhand.rotateCamera(_spot, _saxis, deltaSAngle, 0f);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
protected Vector3f _spot, _paxis, _saxis;
|
||||
protected float _pangle, _pangvel, _protated;
|
||||
protected float _sangle, _sangvel, _srotated;
|
||||
protected Vector3f _pan, _panvel, _panned;
|
||||
protected float _zoom, _zoomvel, _zoomed;
|
||||
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
protected Vector3f _deltaPan = new Vector3f();
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.chat;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.jmex.bui.BContainer;
|
||||
import com.jmex.bui.BTextArea;
|
||||
import com.jmex.bui.BTextField;
|
||||
import com.jmex.bui.event.ActionEvent;
|
||||
import com.jmex.bui.event.ActionListener;
|
||||
import com.jmex.bui.layout.BorderLayout;
|
||||
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatDirector;
|
||||
import com.threerings.crowd.chat.client.ChatDisplay;
|
||||
import com.threerings.crowd.chat.client.SpeakService;
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.chat.data.ChatMessage;
|
||||
import com.threerings.crowd.chat.data.SystemMessage;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.jme.JmeContext;
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Displays chat messages and allows for their input.
|
||||
*/
|
||||
public class ChatView extends BContainer
|
||||
implements ChatDisplay
|
||||
{
|
||||
public ChatView (JmeContext ctx, ChatDirector chatdtr)
|
||||
{
|
||||
_chatdtr = chatdtr;
|
||||
setLayoutManager(new BorderLayout(2, 2));
|
||||
add(_text = new BTextArea(), BorderLayout.CENTER);
|
||||
add(_input = new BTextField(), BorderLayout.SOUTH);
|
||||
|
||||
_input.addListener(new ActionListener() {
|
||||
public void actionPerformed (ActionEvent event) {
|
||||
if (handleInput(_input.getText())) {
|
||||
_input.setText("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
setSpeakService(plobj.speakService);
|
||||
}
|
||||
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
clearSpeakService();
|
||||
}
|
||||
|
||||
public void setSpeakService (SpeakService spsvc)
|
||||
{
|
||||
if (spsvc != null) {
|
||||
_spsvc = spsvc;
|
||||
_chatdtr.addChatDisplay(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSpeakService ()
|
||||
{
|
||||
if (_spsvc != null) {
|
||||
_chatdtr.removeChatDisplay(this);
|
||||
_spsvc = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs our chat input field to request focus.
|
||||
*/
|
||||
public void requestFocus ()
|
||||
{
|
||||
_input.requestFocus();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public void clear ()
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public void displayMessage (ChatMessage msg)
|
||||
{
|
||||
// we may be restricted in the chat types we handle
|
||||
if (!handlesType(msg.localtype)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg instanceof UserMessage) {
|
||||
UserMessage umsg = (UserMessage) msg;
|
||||
if (umsg.localtype == ChatCodes.USER_CHAT_TYPE) {
|
||||
append("[" + umsg.speaker + " whispers] ", ColorRGBA.green);
|
||||
append(umsg.message + "\n");
|
||||
} else {
|
||||
append("<" + umsg.speaker + "> ", ColorRGBA.blue);
|
||||
append(umsg.message + "\n");
|
||||
}
|
||||
|
||||
} else if (msg instanceof SystemMessage) {
|
||||
append(msg.message + "\n", ColorRGBA.red);
|
||||
|
||||
} else {
|
||||
Log.warning("Received unknown message type: " + msg + ".");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setEnabled (boolean enabled)
|
||||
{
|
||||
_input.setEnabled(enabled);
|
||||
}
|
||||
|
||||
protected void displayError (String message)
|
||||
{
|
||||
append(message + "\n", ColorRGBA.red);
|
||||
}
|
||||
|
||||
protected void append (String text, ColorRGBA color)
|
||||
{
|
||||
_text.appendText(text, color);
|
||||
}
|
||||
|
||||
protected void append (String text)
|
||||
{
|
||||
_text.appendText(text);
|
||||
}
|
||||
|
||||
protected boolean handlesType (String localType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean handleInput (String text)
|
||||
{
|
||||
if (text.length() == 0) {
|
||||
// no empty banter
|
||||
return false;
|
||||
}
|
||||
String msg = _chatdtr.requestChat(_spsvc, text, true);
|
||||
if (msg.equals(ChatCodes.SUCCESS)) {
|
||||
return true;
|
||||
} else {
|
||||
_chatdtr.displayFeedback(null, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected ChatDirector _chatdtr;
|
||||
protected SpeakService _spsvc;
|
||||
protected BTextArea _text;
|
||||
protected BTextField _input;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.effect;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Geometry;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.shape.Quad;
|
||||
import com.jme.scene.state.AlphaState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
|
||||
import com.threerings.jme.util.LinearTimeFunction;
|
||||
import com.threerings.jme.util.TimeFunction;
|
||||
|
||||
/**
|
||||
* Fades a supplied quad (or one that covers the screen) in from a solid color
|
||||
* or out to a solid color.
|
||||
*/
|
||||
public class FadeInOutEffect extends Node
|
||||
{
|
||||
public FadeInOutEffect (ColorRGBA color, float startAlpha, float endAlpha,
|
||||
float duration, boolean overUI)
|
||||
{
|
||||
this(color, new LinearTimeFunction(startAlpha, endAlpha, duration),
|
||||
overUI);
|
||||
}
|
||||
|
||||
public FadeInOutEffect (ColorRGBA color, TimeFunction alphaFunc,
|
||||
boolean overUI)
|
||||
{
|
||||
this(null, color, alphaFunc, overUI);
|
||||
setQuad(createCurtain());
|
||||
}
|
||||
|
||||
public FadeInOutEffect (Quad quad, ColorRGBA color, TimeFunction alphaFunc,
|
||||
boolean overUI)
|
||||
{
|
||||
super("FadeInOut");
|
||||
|
||||
_color = new ColorRGBA(
|
||||
color.r, color.g, color.b, alphaFunc.getValue(0));
|
||||
_alphaFunc = alphaFunc;
|
||||
|
||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
||||
_astate = ds.getRenderer().createAlphaState();
|
||||
_astate.setBlendEnabled(true);
|
||||
_astate.setSrcFunction(AlphaState.SB_SRC_ALPHA);
|
||||
_astate.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
|
||||
_astate.setEnabled(true);
|
||||
|
||||
if (quad != null) {
|
||||
setQuad(quad);
|
||||
}
|
||||
|
||||
setRenderQueueMode(Renderer.QUEUE_ORTHO);
|
||||
setZOrder(overUI ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the quad that will be faded in or out.
|
||||
*/
|
||||
public void setQuad (Quad quad)
|
||||
{
|
||||
attachChild(quad);
|
||||
quad.setDefaultColor(_color);
|
||||
quad.setRenderState(_astate);
|
||||
quad.updateRenderState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the fade to be paused.
|
||||
*/
|
||||
public void setPaused (boolean paused)
|
||||
{
|
||||
_paused = paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the fade is paused.
|
||||
*/
|
||||
public boolean isPaused ()
|
||||
{
|
||||
return _paused;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void updateGeometricState (float time, boolean initiator)
|
||||
{
|
||||
super.updateGeometricState(time, initiator);
|
||||
if (_paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
float alpha = _alphaFunc.getValue(time);
|
||||
_color.a = Math.min(1f, Math.max(0f, alpha));
|
||||
if (_alphaFunc.isComplete()) {
|
||||
fadeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called (only once) when we have reached the end of our fade.
|
||||
* Automatically detaches this effect from the hierarchy.
|
||||
*/
|
||||
protected void fadeComplete ()
|
||||
{
|
||||
getParent().detachChild(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a quad that covers the entire screen for full-screen fades.
|
||||
*/
|
||||
protected Quad createCurtain ()
|
||||
{
|
||||
// create a quad the size of the screen
|
||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
||||
float width = ds.getWidth(), height = ds.getHeight();
|
||||
Quad curtain = new Quad("curtain" + hashCode(), width, height);
|
||||
curtain.setLocalTranslation(new Vector3f(width/2, height/2, 0f));
|
||||
return curtain;
|
||||
}
|
||||
|
||||
protected ColorRGBA _color;
|
||||
protected AlphaState _astate;
|
||||
protected TimeFunction _alphaFunc;
|
||||
protected boolean _paused;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.effect;
|
||||
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.system.DisplaySystem;
|
||||
|
||||
import com.jmex.bui.BWindow;
|
||||
|
||||
import com.threerings.jme.util.LinearTimeFunction;
|
||||
import com.threerings.jme.util.TimeFunction;
|
||||
|
||||
/**
|
||||
* Slides a window onto the center of the screen from offscreen or offscreen
|
||||
* from the center of the screen.
|
||||
*/
|
||||
public class WindowSlider extends Node
|
||||
{
|
||||
public static final int FROM_TOP = 0;
|
||||
public static final int FROM_RIGHT = 1;
|
||||
public static final int TO_TOP = 2;
|
||||
public static final int TO_RIGHT = 3;
|
||||
|
||||
/**
|
||||
* Creates a window slider with the specified mode and window that will
|
||||
* slide the window either onto or off of the screen in the specified
|
||||
* number of seconds.
|
||||
*
|
||||
* @param dx an offset applied to the starting or destination position
|
||||
* along the x axis (starting for sliding off, destination for sliding on).
|
||||
* @param dy an offset applied along the y axis.
|
||||
*/
|
||||
public WindowSlider (BWindow window, int mode, float duration,
|
||||
int dx, int dy)
|
||||
{
|
||||
super("slider");
|
||||
|
||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
||||
int swidth = ds.getWidth(), sheight = ds.getHeight();
|
||||
int wwidth = window.getWidth(), wheight = window.getHeight();
|
||||
|
||||
int start = 0, end = 0;
|
||||
switch (mode) {
|
||||
case FROM_TOP:
|
||||
start = sheight+wheight;
|
||||
end = (sheight-wheight)/2 + dy;
|
||||
window.setLocation((swidth-wwidth)/2 + dx, start);
|
||||
break;
|
||||
|
||||
case FROM_RIGHT:
|
||||
start = swidth+wwidth;
|
||||
end = (swidth-wwidth)/2 + dx;
|
||||
window.setLocation(start, (sheight-wheight)/2 + dy);
|
||||
break;
|
||||
|
||||
case TO_TOP:
|
||||
start = (sheight-wheight)/2 + dy;
|
||||
end = sheight+wheight;
|
||||
window.setLocation((swidth-wwidth)/2 + dx, start);
|
||||
break;
|
||||
|
||||
case TO_RIGHT:
|
||||
start = (swidth-wwidth)/2 + dx;
|
||||
end = swidth+wwidth;
|
||||
window.setLocation(start, (sheight-wheight)/2 + dy);
|
||||
break;
|
||||
}
|
||||
|
||||
_mode = mode;
|
||||
_window = window;
|
||||
_tfunc = new LinearTimeFunction(start, end, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows some number of ticks to be skipped to give the window that is
|
||||
* being slid a chance to be layed out before we start keeping track of
|
||||
* time. The layout may be expensive and cause the frame rate to drop for a
|
||||
* frame or two, thus booching our smooth sliding onto the screen.
|
||||
*/
|
||||
public void setSkipTicks (int skipTicks)
|
||||
{
|
||||
_skipTicks = skipTicks;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void updateGeometricState (float time, boolean initiator)
|
||||
{
|
||||
super.updateGeometricState(time, initiator);
|
||||
|
||||
// skip ticks as long as we need to
|
||||
if (_skipTicks-- > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int winx, winy;
|
||||
if (_mode % 2 == 1) {
|
||||
winx = (int)_tfunc.getValue(time);
|
||||
winy = _window.getY();
|
||||
} else {
|
||||
winx = _window.getX();
|
||||
winy = (int)_tfunc.getValue(time);
|
||||
}
|
||||
_window.setLocation(winx, winy);
|
||||
|
||||
if (_tfunc.isComplete()) {
|
||||
slideComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called (only once) when we have reached the end of our slide.
|
||||
* Automatically detaches this effect from the hierarchy.
|
||||
*/
|
||||
protected void slideComplete ()
|
||||
{
|
||||
getParent().detachChild(this);
|
||||
}
|
||||
|
||||
protected int _mode;
|
||||
protected BWindow _window;
|
||||
protected TimeFunction _tfunc;
|
||||
|
||||
// skip two frames by default as that generally handles the normal window
|
||||
// layout process
|
||||
protected int _skipTicks = 2;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Geometry;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
/**
|
||||
* A model controller whose target represents an emitter.
|
||||
*/
|
||||
public abstract class EmissionController extends ModelController
|
||||
{
|
||||
@Override // documentation inherited
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
// substitute underlying mesh for geometry targets
|
||||
if (target instanceof ModelNode) {
|
||||
Spatial mesh = ((ModelNode)target).getChild("mesh");
|
||||
if (mesh != null) {
|
||||
target = mesh;
|
||||
}
|
||||
}
|
||||
super.configure(props, target);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void init (Model model)
|
||||
{
|
||||
super.init(model);
|
||||
_target.setCullMode(Spatial.CULL_ALWAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current location of the emitter in world coordinates.
|
||||
*/
|
||||
protected void getEmitterLocation (Vector3f result)
|
||||
{
|
||||
result.set(_target.getWorldTranslation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current direction of the emitter in world coordinates.
|
||||
*/
|
||||
protected void getEmitterDirection (Vector3f result)
|
||||
{
|
||||
if (_target instanceof Geometry) {
|
||||
BufferUtils.populateFromBuffer(result,
|
||||
((Geometry)_target).getNormalBuffer(0), 0);
|
||||
} else {
|
||||
result.set(0f, 0f, -1f);
|
||||
}
|
||||
_target.getWorldRotation().multLocal(result);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The superclass of procedural animation controllers for models.
|
||||
*/
|
||||
public abstract class ModelController extends Controller
|
||||
implements Externalizable
|
||||
{
|
||||
/**
|
||||
* Configures this controller based on the supplied (sub-)properties and
|
||||
* controller target.
|
||||
*/
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
_target = target;
|
||||
String[] anims = StringUtil.parseStringArray(
|
||||
props.getProperty("animations", ""));
|
||||
if (anims.length == 0) {
|
||||
return;
|
||||
}
|
||||
_animations = new HashSet<String>();
|
||||
Collections.addAll(_animations, anims);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the controller's target.
|
||||
*/
|
||||
public Spatial getTarget ()
|
||||
{
|
||||
return _target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves any textures required by the controller.
|
||||
*/
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this controller.
|
||||
*/
|
||||
public void init (Model model)
|
||||
{
|
||||
model.addAnimationObserver(_animobs);
|
||||
if (_animations != null) {
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or populates and returns a clone of this object using the given
|
||||
* clone properties.
|
||||
*
|
||||
* @param store an instance of this class to populate, or <code>null</code>
|
||||
* to create a new instance
|
||||
*/
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
if (store == null) {
|
||||
return null;
|
||||
}
|
||||
ModelController mstore = (ModelController)store;
|
||||
mstore._target = ((ModelSpatial)_target).putClone(null, properties);
|
||||
mstore._animations = _animations;
|
||||
return mstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeObject(_target);
|
||||
out.writeObject(_animations);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
_target = (Spatial)in.readObject();
|
||||
_animations = (HashSet<String>)in.readObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation is started on the model.
|
||||
*/
|
||||
protected void animationStarted (String anim)
|
||||
{
|
||||
if (_animations != null && _animations.contains(anim)) {
|
||||
setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation is stopped on the model.
|
||||
*/
|
||||
protected void animationStopped (String anim)
|
||||
{
|
||||
if (_animations != null) {
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** The target to control. */
|
||||
protected Spatial _target;
|
||||
|
||||
/** The animations for which this controller should be active, or
|
||||
* <code>null</code> for all of them. */
|
||||
protected HashSet<String> _animations;
|
||||
|
||||
/** Listens to the model's animation state. */
|
||||
protected Model.AnimationObserver _animobs =
|
||||
new Model.AnimationObserver() {
|
||||
public boolean animationStarted (Model model, String anim) {
|
||||
ModelController.this.animationStarted(anim);
|
||||
return true;
|
||||
}
|
||||
public boolean animationCompleted (Model model, String anim) {
|
||||
animationStopped(anim);
|
||||
return true;
|
||||
}
|
||||
public boolean animationCancelled (Model model, String anim) {
|
||||
animationStopped(anim);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.SharedMesh;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.TriMesh;
|
||||
import com.jme.scene.VBOInfo;
|
||||
import com.jme.scene.batch.TriangleBatch;
|
||||
import com.jme.scene.state.AlphaState;
|
||||
import com.jme.scene.state.CullState;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.scene.state.TextureState;
|
||||
import com.jme.scene.state.ZBufferState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A {@link TriMesh} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelMesh extends TriMesh
|
||||
implements Externalizable, ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelMesh ()
|
||||
{
|
||||
super("mesh");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mesh with no vertex data.
|
||||
*/
|
||||
public ModelMesh (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this mesh based on the given parameters and (sub-)properties.
|
||||
*
|
||||
* @param texture the texture specified in the model export, if any (can be
|
||||
* overridden by textures specified in the properties)
|
||||
* @param solid whether or not the mesh allows back face culling
|
||||
* @param transparent whether or not the mesh is (partially) transparent
|
||||
*/
|
||||
public void configure (
|
||||
boolean solid, String texture, boolean transparent, Properties props)
|
||||
{
|
||||
_textures = (texture == null) ? null : StringUtil.parseStringArray(
|
||||
props.getProperty(texture, texture));
|
||||
_solid = solid;
|
||||
_transparent = transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the buffers as {@link ByteBuffer}s, because we can't create byte
|
||||
* views of non-byte buffers. This method is where the model is
|
||||
* initialized after loading.
|
||||
*/
|
||||
public void reconstruct (
|
||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
||||
ByteBuffer textures, ByteBuffer indices)
|
||||
{
|
||||
reconstruct(
|
||||
vertices == null ? null : vertices.asFloatBuffer(),
|
||||
normals == null ? null : normals.asFloatBuffer(),
|
||||
colors == null ? null : colors.asFloatBuffer(),
|
||||
textures == null ? null : textures.asFloatBuffer(),
|
||||
indices == null ? null : indices.asIntBuffer());
|
||||
_vertexByteBuffer = vertices;
|
||||
_normalByteBuffer = normals;
|
||||
_colorByteBuffer = colors;
|
||||
_textureByteBuffer = textures;
|
||||
_indexByteBuffer = indices;
|
||||
|
||||
// initialize the model if we're displaying
|
||||
if (DisplaySystem.getDisplaySystem() == null) {
|
||||
return;
|
||||
}
|
||||
if (_backCull == null) {
|
||||
initSharedStates();
|
||||
}
|
||||
if (_solid) {
|
||||
setRenderState(_backCull);
|
||||
}
|
||||
if (_transparent) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
setRenderState(_blendAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the vertices and the transform of the mesh so that the mesh's
|
||||
* position lies at the center of its bounding volume.
|
||||
*/
|
||||
public void centerVertices ()
|
||||
{
|
||||
Vector3f offset = getBatch(0).getModelBound().getCenter().negate();
|
||||
if (!offset.equals(Vector3f.ZERO)) {
|
||||
getLocalTranslation().subtractLocal(offset);
|
||||
getBatch(0).getModelBound().getCenter().set(Vector3f.ZERO);
|
||||
getBatch(0).translatePoints(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures, IntBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
|
||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
||||
_indexBufferSize = (indices == null) ? 0 : indices.capacity();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures);
|
||||
|
||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
||||
{
|
||||
ModelMesh mstore = (ModelMesh)properties.originalToCopy.get(this);
|
||||
if (mstore != null) {
|
||||
return mstore;
|
||||
} else if (store == null) {
|
||||
mstore = new ModelMesh(getName());
|
||||
} else {
|
||||
mstore = (ModelMesh)store;
|
||||
}
|
||||
properties.originalToCopy.put(this, mstore);
|
||||
mstore.normalsMode = normalsMode;
|
||||
mstore.cullMode = cullMode;
|
||||
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
|
||||
RenderState rstate = getRenderState(ii);
|
||||
if (rstate != null) {
|
||||
mstore.setRenderState(rstate);
|
||||
}
|
||||
}
|
||||
mstore.renderQueueMode = renderQueueMode;
|
||||
mstore.lockedMode = lockedMode;
|
||||
mstore.lightCombineMode = lightCombineMode;
|
||||
mstore.textureCombineMode = textureCombineMode;
|
||||
mstore.name = name;
|
||||
mstore.isCollidable = isCollidable;
|
||||
mstore.localRotation.set(localRotation);
|
||||
mstore.localTranslation.set(localTranslation);
|
||||
mstore.localScale.set(localScale);
|
||||
for (Object controller : getControllers()) {
|
||||
if (controller instanceof ModelController) {
|
||||
mstore.addController(
|
||||
((ModelController)controller).putClone(null, properties));
|
||||
}
|
||||
}
|
||||
TriangleBatch batch = (TriangleBatch)getBatch(0),
|
||||
mbatch = (TriangleBatch)mstore.getBatch(0);
|
||||
mbatch.setVertexBuffer(properties.isSet("vertices") ?
|
||||
batch.getVertexBuffer() :
|
||||
BufferUtils.clone(batch.getVertexBuffer()));
|
||||
mbatch.setColorBuffer(properties.isSet("colors") ?
|
||||
batch.getColorBuffer() :
|
||||
BufferUtils.clone(batch.getColorBuffer()));
|
||||
mbatch.setNormalBuffer(properties.isSet("normals") ?
|
||||
batch.getNormalBuffer() :
|
||||
BufferUtils.clone(batch.getNormalBuffer()));
|
||||
FloatBuffer texcoords;
|
||||
for (int ii = 0; (texcoords = batch.getTextureBuffer(ii)) != null;
|
||||
ii++) {
|
||||
mbatch.setTextureBuffer(properties.isSet("texcoords") ?
|
||||
texcoords : BufferUtils.clone(texcoords), ii);
|
||||
}
|
||||
mbatch.setIndexBuffer(properties.isSet("indices") ?
|
||||
batch.getIndexBuffer() :
|
||||
BufferUtils.clone(batch.getIndexBuffer()));
|
||||
if (properties.isSet("vboinfo")) {
|
||||
mbatch.setVBOInfo(batch.getVBOInfo());
|
||||
}
|
||||
if (properties.isSet("obbtree")) {
|
||||
mbatch.setCollisionTree(batch.getCollisionTree());
|
||||
}
|
||||
if (properties.isSet("displaylistid")) {
|
||||
mbatch.setDisplayListID(batch.getDisplayListID());
|
||||
}
|
||||
if (batch.getModelBound() != null) {
|
||||
mbatch.setModelBound(properties.isSet("bound") ?
|
||||
batch.getModelBound() : batch.getModelBound().clone(null));
|
||||
}
|
||||
if (_textures != null && _textures.length > 1) {
|
||||
int tidx = properties.random % _textures.length;
|
||||
mstore._textures = new String[] { _textures[tidx] };
|
||||
mstore._tstates = new TextureState[] { _tstates[tidx] };
|
||||
mstore.setRenderState(_tstates[tidx]);
|
||||
} else {
|
||||
mstore._textures = _textures;
|
||||
mstore._tstates = _tstates;
|
||||
}
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
if (!_transformLocked) {
|
||||
super.updateWorldVectors();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeUTF(getName());
|
||||
out.writeObject(getLocalTranslation());
|
||||
out.writeObject(getLocalRotation());
|
||||
out.writeObject(getLocalScale());
|
||||
out.writeObject(getBatch(0).getModelBound());
|
||||
out.writeInt(_vertexBufferSize);
|
||||
out.writeInt(_normalBufferSize);
|
||||
out.writeInt(_colorBufferSize);
|
||||
out.writeInt(_textureBufferSize);
|
||||
out.writeInt(_indexBufferSize);
|
||||
out.writeObject(_textures);
|
||||
out.writeBoolean(_solid);
|
||||
out.writeBoolean(_transparent);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
setName(in.readUTF());
|
||||
setLocalTranslation((Vector3f)in.readObject());
|
||||
setLocalRotation((Quaternion)in.readObject());
|
||||
setLocalScale((Vector3f)in.readObject());
|
||||
setModelBound((BoundingVolume)in.readObject());
|
||||
_vertexBufferSize = in.readInt();
|
||||
_normalBufferSize = in.readInt();
|
||||
_colorBufferSize = in.readInt();
|
||||
_textureBufferSize = in.readInt();
|
||||
_indexBufferSize = in.readInt();
|
||||
_textures = (String[])in.readObject();
|
||||
_solid = in.readBoolean();
|
||||
_transparent = in.readBoolean();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
if (useVBOs && renderer.supportsVBO()) {
|
||||
VBOInfo vboinfo = new VBOInfo(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
setVBOInfo(vboinfo);
|
||||
|
||||
} else if (useDisplayLists) {
|
||||
lockMeshes(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
if (_textures == null) {
|
||||
return;
|
||||
}
|
||||
_tstates = new TextureState[_textures.length];
|
||||
for (int ii = 0; ii < _textures.length; ii++) {
|
||||
_tstates[ii] = tprov.getTexture(_textures[ii]);
|
||||
}
|
||||
if (_tstates[0] != null) {
|
||||
setRenderState(_tstates[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setMeshFrame (int frameId)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException
|
||||
{
|
||||
if (_vertexBufferSize > 0) {
|
||||
_vertexByteBuffer.rewind();
|
||||
convertOrder(_vertexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_vertexByteBuffer);
|
||||
convertOrder(_vertexByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
_normalByteBuffer.rewind();
|
||||
convertOrder(_normalByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_normalByteBuffer);
|
||||
convertOrder(_normalByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
_colorByteBuffer.rewind();
|
||||
convertOrder(_colorByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_colorByteBuffer);
|
||||
convertOrder(_colorByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
_textureByteBuffer.rewind();
|
||||
convertOrder(_textureByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_textureByteBuffer);
|
||||
convertOrder(_textureByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
_indexByteBuffer.rewind();
|
||||
convertOrder(_indexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
||||
out.write(_indexByteBuffer);
|
||||
convertOrder(_indexByteBuffer, ByteOrder.nativeOrder());
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException
|
||||
{
|
||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
||||
ibbuf = null;
|
||||
ByteOrder le = ByteOrder.LITTLE_ENDIAN;
|
||||
if (_vertexBufferSize > 0) {
|
||||
vbbuf = ByteBuffer.allocateDirect(_vertexBufferSize*4).order(le);
|
||||
in.read(vbbuf);
|
||||
vbbuf.rewind();
|
||||
convertOrder(vbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
nbbuf = ByteBuffer.allocateDirect(_normalBufferSize*4).order(le);
|
||||
in.read(nbbuf);
|
||||
nbbuf.rewind();
|
||||
convertOrder(nbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
cbbuf = ByteBuffer.allocateDirect(_colorBufferSize*4).order(le);
|
||||
in.read(cbbuf);
|
||||
cbbuf.rewind();
|
||||
convertOrder(cbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
tbbuf = ByteBuffer.allocateDirect(_textureBufferSize*4).order(le);
|
||||
in.read(tbbuf);
|
||||
tbbuf.rewind();
|
||||
convertOrder(tbbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
ibbuf = ByteBuffer.allocateDirect(_indexBufferSize*4).order(le);
|
||||
in.read(ibbuf);
|
||||
ibbuf.rewind();
|
||||
convertOrder(ibbuf, ByteOrder.nativeOrder());
|
||||
}
|
||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void sliceBuffers (MappedByteBuffer map)
|
||||
{
|
||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
||||
ibbuf = null;
|
||||
int total = 0;
|
||||
if (_vertexBufferSize > 0) {
|
||||
int npos = map.position() + _vertexBufferSize*4;
|
||||
map.limit(npos);
|
||||
vbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_normalBufferSize > 0) {
|
||||
int npos = map.position() + _normalBufferSize*4;
|
||||
map.limit(npos);
|
||||
nbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_colorBufferSize > 0) {
|
||||
int npos = map.position() + _colorBufferSize*4;
|
||||
map.limit(npos);
|
||||
cbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_textureBufferSize > 0) {
|
||||
int npos = map.position() + _textureBufferSize*4;
|
||||
map.limit(npos);
|
||||
tbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
if (_indexBufferSize > 0) {
|
||||
int npos = map.position() + _indexBufferSize*4;
|
||||
map.limit(npos);
|
||||
ibbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
||||
map.position(npos);
|
||||
}
|
||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the transform and bounds of this mesh on the assumption that its
|
||||
* position will not change.
|
||||
*/
|
||||
protected void lockInstance ()
|
||||
{
|
||||
lockBounds();
|
||||
_transformLocked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imposes the specified order on the given buffer of 32 bit values.
|
||||
*/
|
||||
protected static void convertOrder (ByteBuffer buf, ByteOrder order)
|
||||
{
|
||||
if (buf.order() == order) {
|
||||
return;
|
||||
}
|
||||
IntBuffer obuf = buf.asIntBuffer(),
|
||||
nbuf = buf.order(order).asIntBuffer();
|
||||
while (obuf.hasRemaining()) {
|
||||
nbuf.put(obuf.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the states shared between all models. Requires an active
|
||||
* display.
|
||||
*/
|
||||
protected static void initSharedStates ()
|
||||
{
|
||||
Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
|
||||
_backCull = renderer.createCullState();
|
||||
_backCull.setCullMode(CullState.CS_BACK);
|
||||
_blendAlpha = renderer.createAlphaState();
|
||||
_blendAlpha.setBlendEnabled(true);
|
||||
_overlayZBuffer = renderer.createZBufferState();
|
||||
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
|
||||
_overlayZBuffer.setWritable(false);
|
||||
}
|
||||
|
||||
/** The sizes of the various buffers (zero for <code>null</code>). */
|
||||
protected int _vertexBufferSize, _normalBufferSize, _colorBufferSize,
|
||||
_textureBufferSize, _indexBufferSize;
|
||||
|
||||
/** The backing byte buffers for the various buffers. */
|
||||
protected ByteBuffer _vertexByteBuffer, _normalByteBuffer,
|
||||
_colorByteBuffer, _textureByteBuffer, _indexByteBuffer;
|
||||
|
||||
/** The type of bounding volume that this mesh should use. */
|
||||
protected int _boundingType;
|
||||
|
||||
/** The name of this model's textures, or <code>null</code> for none. */
|
||||
protected String[] _textures;
|
||||
|
||||
/** Whether or not this mesh can enable back-face culling. */
|
||||
protected boolean _solid;
|
||||
|
||||
/** Whether or not this mesh must be rendered as transparent. */
|
||||
protected boolean _transparent;
|
||||
|
||||
/** For prototype meshes, the resolved texture states. */
|
||||
protected TextureState[] _tstates;
|
||||
|
||||
/** Whether or not the transform has been locked. This operates in a
|
||||
* slightly different way than JME's locking, in that it allows applying
|
||||
* transformations to display lists. */
|
||||
protected boolean _transformLocked;
|
||||
|
||||
/** The shared state for back face culling. */
|
||||
protected static CullState _backCull;
|
||||
|
||||
/** The shared state for alpha blending. */
|
||||
protected static AlphaState _blendAlpha;
|
||||
|
||||
/** The shared state for checking, but not writing to, the z buffer. */
|
||||
protected static ZBufferState _overlayZBuffer;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.state.RenderState;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A {@link Node} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelNode extends Node
|
||||
implements Externalizable, ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelNode ()
|
||||
{
|
||||
super("node");
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*/
|
||||
public ModelNode (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches the scene graph rooted at this node for a
|
||||
* node with the provided name.
|
||||
*/
|
||||
public Spatial getDescendant (String name)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child.getName().equals(name)) {
|
||||
return child;
|
||||
} else if (child instanceof ModelNode) {
|
||||
child = ((ModelNode)child).getDescendant(name);
|
||||
if (child != null) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the model space transform of the node.
|
||||
*/
|
||||
public Matrix4f getModelTransform ()
|
||||
{
|
||||
return _modelTransform;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldData (float time)
|
||||
{
|
||||
// we use locked bounds as an indication that we can skip the update
|
||||
// altogether
|
||||
if ((lockedMode & LOCKED_BOUNDS) == 0) {
|
||||
super.updateWorldData(time);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldBound ()
|
||||
{
|
||||
// if the node is culled, there are no mesh descendants and thus no
|
||||
// bounds
|
||||
if (cullMode != CULL_ALWAYS) {
|
||||
super.updateWorldBound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
super.updateWorldVectors();
|
||||
if (parent instanceof ModelNode) {
|
||||
setTransform(getLocalTranslation(), getLocalRotation(),
|
||||
getLocalScale(), _localTransform);
|
||||
((ModelNode)parent).getModelTransform().mult(_localTransform,
|
||||
_modelTransform);
|
||||
|
||||
} else {
|
||||
_modelTransform.loadIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
||||
{
|
||||
ModelNode mstore = (ModelNode)properties.originalToCopy.get(this);
|
||||
if (mstore != null) {
|
||||
return mstore;
|
||||
} else if (store == null) {
|
||||
mstore = new ModelNode(getName());
|
||||
} else {
|
||||
mstore = (ModelNode)store;
|
||||
}
|
||||
properties.originalToCopy.put(this, mstore);
|
||||
mstore.normalsMode = normalsMode;
|
||||
mstore.cullMode = cullMode;
|
||||
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
|
||||
RenderState rstate = getRenderState(ii);
|
||||
if (rstate != null) {
|
||||
mstore.setRenderState(rstate);
|
||||
}
|
||||
}
|
||||
mstore.renderQueueMode = renderQueueMode;
|
||||
mstore.lockedMode = lockedMode;
|
||||
mstore.lightCombineMode = lightCombineMode;
|
||||
mstore.textureCombineMode = textureCombineMode;
|
||||
mstore.name = name;
|
||||
mstore.isCollidable = isCollidable;
|
||||
mstore.localRotation.set(localRotation);
|
||||
mstore.localTranslation.set(localTranslation);
|
||||
mstore.localScale.set(localScale);
|
||||
for (Object controller : getControllers()) {
|
||||
if (controller instanceof ModelController) {
|
||||
mstore.addController(
|
||||
((ModelController)controller).putClone(null, properties));
|
||||
}
|
||||
}
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
mstore.attachChild(
|
||||
((ModelSpatial)child).putClone(null, properties));
|
||||
}
|
||||
}
|
||||
return mstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeUTF(getName());
|
||||
out.writeObject(getLocalTranslation());
|
||||
out.writeObject(getLocalRotation());
|
||||
out.writeObject(getLocalScale());
|
||||
out.writeObject(getChildren());
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
setName(in.readUTF());
|
||||
setLocalTranslation((Vector3f)in.readObject());
|
||||
setLocalRotation((Quaternion)in.readObject());
|
||||
setLocalScale((Vector3f)in.readObject());
|
||||
ArrayList<Spatial> children = (ArrayList<Spatial>)in.readObject();
|
||||
if (children != null) {
|
||||
for (Spatial child : children) {
|
||||
attachChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).expandModelBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
updateWorldVectors();
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).setReferenceTransforms();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).lockStaticMeshes(renderer, useVBOs,
|
||||
useDisplayLists);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).resolveTextures(tprov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).storeMeshFrame(frameId, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void setMeshFrame (int frameId)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).setMeshFrame(frameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).blendMeshFrames(
|
||||
frameId1, frameId2, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).writeBuffers(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).readBuffers(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void sliceBuffers (MappedByteBuffer map)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).sliceBuffers(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cull state of any nodes that do not contain geometric
|
||||
* descendants to {@link CULL_ALWAYS} so that they don't waste
|
||||
* rendering time.
|
||||
*
|
||||
* @return true if this node should be drawn, false if it contains
|
||||
* no mesh descendants
|
||||
*/
|
||||
protected boolean cullInvisibleNodes ()
|
||||
{
|
||||
boolean hasVisibleDescendants = false;
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (!(child instanceof ModelNode) ||
|
||||
((ModelNode)child).cullInvisibleNodes()) {
|
||||
hasVisibleDescendants = true;
|
||||
}
|
||||
}
|
||||
setCullMode(hasVisibleDescendants ? CULL_INHERIT : CULL_ALWAYS);
|
||||
return hasVisibleDescendants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the transforms and bounds of this instance with the assumption
|
||||
* that the position will never change.
|
||||
*
|
||||
* @param targets the targets of the model's controllers, which determine
|
||||
* the subset of nodes that can be locked
|
||||
* @return true if this node is a target or contains any targets, otherwise
|
||||
* false
|
||||
*/
|
||||
protected boolean lockInstance (HashSet<Spatial> targets)
|
||||
{
|
||||
updateWorldVectors();
|
||||
lockedMode |= LOCKED_TRANSFORMS;
|
||||
|
||||
boolean containsTargets = false;
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (targets.contains(child) || (child instanceof ModelNode &&
|
||||
((ModelNode)child).lockInstance(targets))) {
|
||||
containsTargets = true;
|
||||
|
||||
} else if (child instanceof ModelMesh) {
|
||||
((ModelMesh)child).lockInstance();
|
||||
}
|
||||
}
|
||||
if (containsTargets) {
|
||||
return true;
|
||||
} else {
|
||||
updateWorldBound();
|
||||
lockedMode |= LOCKED_BOUNDS;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a matrix to the transform defined by the given translation,
|
||||
* rotation, and scale values.
|
||||
*/
|
||||
protected static Matrix4f setTransform (
|
||||
Vector3f translation, Quaternion rotation, Vector3f scale,
|
||||
Matrix4f result)
|
||||
{
|
||||
result.set(rotation);
|
||||
result.setTranslation(translation);
|
||||
|
||||
result.m00 *= scale.x;
|
||||
result.m01 *= scale.y;
|
||||
result.m02 *= scale.z;
|
||||
|
||||
result.m10 *= scale.x;
|
||||
result.m11 *= scale.y;
|
||||
result.m12 *= scale.z;
|
||||
|
||||
result.m20 *= scale.x;
|
||||
result.m21 *= scale.y;
|
||||
result.m22 *= scale.z;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** The node's transform in local and model space. */
|
||||
protected Matrix4f _localTransform = new Matrix4f(),
|
||||
_modelTransform = new Matrix4f();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
/**
|
||||
* Contains method common to both {@link ModelNode}s and {@link ModelMesh}es.
|
||||
*/
|
||||
public interface ModelSpatial
|
||||
{
|
||||
/**
|
||||
* Recursively expands the model bounds of any deformable meshes so that
|
||||
* they include the current vertex positions.
|
||||
*/
|
||||
public void expandModelBounds ();
|
||||
|
||||
/**
|
||||
* Recursively sets the reference transforms for any bones in the model.
|
||||
*/
|
||||
public void setReferenceTransforms ();
|
||||
|
||||
/**
|
||||
* Recursively compiles any static meshes to vertex buffer objects (VBOs)
|
||||
* or display lists.
|
||||
*
|
||||
* @param useVBOs if true, use VBOs if the graphics card supports them
|
||||
* @param useDisplayLists if true and not using VBOs, compile static
|
||||
* objects to display lists
|
||||
*/
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists);
|
||||
|
||||
/**
|
||||
* Recursively resolves texture references using the given provider.
|
||||
*/
|
||||
public void resolveTextures (TextureProvider tprov);
|
||||
|
||||
/**
|
||||
* Recursively requests that the current state of all skinned meshes be
|
||||
* stored as an animation frame on the next update.
|
||||
*
|
||||
* @param frameId the frame id, which uniquely identifies one frame of
|
||||
* one animation
|
||||
* @param blend whether or not the stored frames will be retrieved by
|
||||
* calls to {@link #blendAnimationFrames} as opposed to
|
||||
* {@link #setAnimationFrames}
|
||||
*/
|
||||
public void storeMeshFrame (int frameId, boolean blend);
|
||||
|
||||
/**
|
||||
* Recursively switches all skinned meshes to a stored animation frame for
|
||||
* the next update.
|
||||
*/
|
||||
public void setMeshFrame (int frameId);
|
||||
|
||||
/**
|
||||
* Recursively blends all skinned meshes between two stored animation
|
||||
* frames for the next update.
|
||||
*/
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha);
|
||||
|
||||
/**
|
||||
* Creates or populates and returns a clone of this object using the given
|
||||
* clone properties.
|
||||
*
|
||||
* @param store an instance of this class to populate, or <code>null</code>
|
||||
* to create a new instance
|
||||
*/
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties);
|
||||
|
||||
/**
|
||||
* Recursively writes any data buffers to the output channel.
|
||||
*/
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Recursively reads any data buffers from the input channel.
|
||||
*/
|
||||
public void readBuffers (FileChannel in)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Recursively slices any data buffers from the buffer map.
|
||||
*/
|
||||
public void sliceBuffers (MappedByteBuffer map);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A procedural animation that rotates a node around at a constant angular
|
||||
* velocity.
|
||||
*/
|
||||
public class Rotator extends ModelController
|
||||
{
|
||||
@Override // documentation inherited
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
String axisstr = props.getProperty("axis", "x"),
|
||||
rpsstr = props.getProperty("radpersec", "3.14");
|
||||
if (axisstr.equalsIgnoreCase("x")) {
|
||||
_axis = Vector3f.UNIT_X;
|
||||
} else if (axisstr.equalsIgnoreCase("y")) {
|
||||
_axis = Vector3f.UNIT_Y;
|
||||
} else if (axisstr.equalsIgnoreCase("z")) {
|
||||
_axis = Vector3f.UNIT_Z;
|
||||
} else {
|
||||
float[] axis = StringUtil.parseFloatArray(axisstr);
|
||||
if (axis != null && axis.length == 3) {
|
||||
_axis = new Vector3f(axis[0], axis[1],
|
||||
axis[2]).normalizeLocal();
|
||||
|
||||
} else {
|
||||
Log.warning("Invalid rotation axis [axis=" + axisstr + "].");
|
||||
}
|
||||
}
|
||||
try {
|
||||
_radpersec = Float.parseFloat(rpsstr);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.warning("Invalid rotation rate [radpersec=" + rpsstr + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_rot.fromAngleNormalAxis(time * _radpersec, _axis);
|
||||
_target.getLocalRotation().multLocal(_rot);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
Rotator rstore;
|
||||
if (store == null) {
|
||||
rstore = new Rotator();
|
||||
} else {
|
||||
rstore = (Rotator)store;
|
||||
}
|
||||
super.putClone(rstore, properties);
|
||||
rstore._axis = _axis;
|
||||
rstore._radpersec = _radpersec;
|
||||
return rstore;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeExternal(out);
|
||||
out.writeObject(_axis);
|
||||
out.writeFloat(_radpersec);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Externalizable
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readExternal(in);
|
||||
_axis = (Vector3f)in.readObject();
|
||||
_radpersec = in.readFloat();
|
||||
}
|
||||
|
||||
/** The axis about which to rotate. */
|
||||
protected Vector3f _axis;
|
||||
|
||||
/** The velocity at which to rotate in radians per second. */
|
||||
protected float _radpersec;
|
||||
|
||||
/** A temporary quaternion. */
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.TriMesh;
|
||||
import com.jme.scene.VBOInfo;
|
||||
import com.jme.scene.batch.SharedBatch;
|
||||
import com.jme.scene.batch.TriangleBatch;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* A triangle mesh that deforms according to a bone hierarchy.
|
||||
*/
|
||||
public class SkinMesh extends ModelMesh
|
||||
{
|
||||
/** Represents the vertex weights of a group of vertices influenced by the
|
||||
* same set of bones. */
|
||||
public static class WeightGroup
|
||||
implements Serializable
|
||||
{
|
||||
/** The number of vertices in this weight group. */
|
||||
public int vertexCount;
|
||||
|
||||
/** The bones influencing this group. */
|
||||
public Bone[] bones;
|
||||
|
||||
/** The array of interleaved weights (of length <code>vertexCount *
|
||||
* boneIndices.length</code>): weights for first vertex, weights for
|
||||
* second, etc. */
|
||||
public float[] weights;
|
||||
|
||||
/**
|
||||
* Rebinds this weight group for a prototype instance.
|
||||
*
|
||||
* @param bmap the mapping from prototype to instance bones
|
||||
*/
|
||||
public WeightGroup rebind (HashMap<Bone, Bone> bmap)
|
||||
{
|
||||
WeightGroup wgroup = new WeightGroup();
|
||||
wgroup.vertexCount = vertexCount;
|
||||
wgroup.bones = new Bone[bones.length];
|
||||
for (int ii = 0; ii < bones.length; ii++) {
|
||||
wgroup.bones[ii] = bmap.get(bones[ii]);
|
||||
}
|
||||
wgroup.weights = weights;
|
||||
return wgroup;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** Represents a bone that influences the mesh. */
|
||||
public static class Bone
|
||||
implements Serializable
|
||||
{
|
||||
/** The node that defines the bone's position. */
|
||||
public ModelNode node;
|
||||
|
||||
/** The inverse of the bone's model space reference transform. */
|
||||
public transient Matrix4f invRefTransform;
|
||||
|
||||
/** The bone's current transform in model space. */
|
||||
public transient Matrix4f transform;
|
||||
|
||||
public Bone (ModelNode node)
|
||||
{
|
||||
this.node = node;
|
||||
transform = new Matrix4f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebinds this bone for a prototype instance.
|
||||
*
|
||||
* @param pnodes a mapping from prototype nodes to instance nodes
|
||||
*/
|
||||
public Bone rebind (HashMap pnodes)
|
||||
{
|
||||
Bone bone = new Bone((ModelNode)pnodes.get(node));
|
||||
bone.invRefTransform = invRefTransform;
|
||||
bone.transform = new Matrix4f();
|
||||
return bone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bone's transient state.
|
||||
*/
|
||||
private void readObject (ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
in.defaultReadObject();
|
||||
transform = new Matrix4f();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public SkinMesh ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty mesh.
|
||||
*/
|
||||
public SkinMesh (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the array of weight groups that determine how bones affect
|
||||
* each vertex.
|
||||
*/
|
||||
public void setWeightGroups (WeightGroup[] weightGroups)
|
||||
{
|
||||
_weightGroups = weightGroups;
|
||||
|
||||
// compile a list of all referenced bones
|
||||
HashSet<Bone> bones = new HashSet<Bone>();
|
||||
for (WeightGroup group : weightGroups) {
|
||||
Collections.addAll(bones, group.bones);
|
||||
}
|
||||
_bones = bones.toArray(new Bone[bones.size()]);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void reconstruct (
|
||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
||||
ByteBuffer textures, ByteBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
|
||||
// store the current buffers as the originals
|
||||
storeOriginalBuffers();
|
||||
|
||||
// initialize the quantized frame table
|
||||
_frames = new HashIntMap<Object>();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void centerVertices ()
|
||||
{
|
||||
super.centerVertices();
|
||||
storeOriginalBuffers();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
||||
{
|
||||
SkinMesh mstore = (SkinMesh)properties.originalToCopy.get(this);
|
||||
if (mstore != null) {
|
||||
return mstore;
|
||||
} else if (store == null) {
|
||||
mstore = new SkinMesh(getName());
|
||||
} else {
|
||||
mstore = (SkinMesh)store;
|
||||
}
|
||||
properties.removeProperty("vertices");
|
||||
properties.removeProperty("normals");
|
||||
properties.removeProperty("displaylistid");
|
||||
super.putClone(mstore, properties);
|
||||
properties.addProperty("vertices");
|
||||
properties.addProperty("normals");
|
||||
properties.addProperty("displaylistid");
|
||||
mstore._frames = _frames;
|
||||
mstore._useDisplayLists = _useDisplayLists;
|
||||
mstore._invRefTransform = _invRefTransform;
|
||||
mstore._bones = new Bone[_bones.length];
|
||||
HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>();
|
||||
for (int ii = 0; ii < _bones.length; ii++) {
|
||||
bmap.put(_bones[ii], mstore._bones[ii] =
|
||||
_bones[ii].rebind(properties.originalToCopy));
|
||||
}
|
||||
mstore._weightGroups = new WeightGroup[_weightGroups.length];
|
||||
for (int ii = 0; ii < _weightGroups.length; ii++) {
|
||||
mstore._weightGroups[ii] = _weightGroups[ii].rebind(bmap);
|
||||
}
|
||||
mstore._ovbuf = _ovbuf;
|
||||
mstore._onbuf = _onbuf;
|
||||
mstore._vbuf = new float[_vbuf.length];
|
||||
mstore._nbuf = new float[_nbuf.length];
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void writeExternal (ObjectOutput out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeExternal(out);
|
||||
out.writeObject(_weightGroups);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void readExternal (ObjectInput in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readExternal(in);
|
||||
setWeightGroups((WeightGroup[])in.readObject());
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
BoundingVolume obound =
|
||||
(BoundingVolume)getBatch(0).getModelBound().clone(null);
|
||||
updateModelBound();
|
||||
getBatch(0).getModelBound().mergeLocal(obound);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
_invRefTransform = new Matrix4f();
|
||||
if (parent instanceof ModelNode) {
|
||||
Matrix4f transform = new Matrix4f();
|
||||
ModelNode.setTransform(getLocalTranslation(), getLocalRotation(),
|
||||
getLocalScale(), transform);
|
||||
((ModelNode)parent).getModelTransform().mult(transform,
|
||||
_invRefTransform);
|
||||
_invRefTransform.invertLocal();
|
||||
}
|
||||
for (Bone bone : _bones) {
|
||||
bone.invRefTransform =
|
||||
_invRefTransform.mult(bone.node.getModelTransform()).invert();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
// we can use VBOs for color, texture, and indices
|
||||
if (useVBOs && renderer.supportsVBO()) {
|
||||
VBOInfo vboinfo = new VBOInfo(false);
|
||||
vboinfo.setVBOColorEnabled(true);
|
||||
vboinfo.setVBOTextureEnabled(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
setVBOInfo(vboinfo);
|
||||
}
|
||||
_useDisplayLists = useDisplayLists;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
_storeFrameId = frameId;
|
||||
_storeBlend = blend;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void setMeshFrame (int frameId)
|
||||
{
|
||||
TriangleBatch batch = getBatch(0),
|
||||
tbatch = (TriangleBatch)_frames.get(frameId);
|
||||
if (batch instanceof SharedBatch) {
|
||||
((SharedBatch)batch).setTarget(tbatch);
|
||||
} else {
|
||||
clearBatches();
|
||||
addBatch(new SharedBatch(tbatch));
|
||||
getBatch(0).updateRenderState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
||||
{
|
||||
BlendFrame frame1 = (BlendFrame)_frames.get(frameId1),
|
||||
frame2 = (BlendFrame)_frames.get(frameId2);
|
||||
frame1.blend(frame2, alpha, _vbuf, _nbuf);
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
vbuf.put(_vbuf);
|
||||
nbuf.rewind();
|
||||
nbuf.put(_nbuf);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void updateWorldData (float time)
|
||||
{
|
||||
super.updateWorldData(time);
|
||||
if (_weightGroups == null || _storeFrameId == -1) {
|
||||
return;
|
||||
}
|
||||
// update the bone transforms
|
||||
for (Bone bone : _bones) {
|
||||
_invRefTransform.mult(bone.node.getModelTransform(),
|
||||
bone.transform);
|
||||
bone.transform.multLocal(bone.invRefTransform);
|
||||
}
|
||||
|
||||
// deform the mesh according to the positions of the bones (this code
|
||||
// is ugly as sin because it's optimized at a low level)
|
||||
Bone[] bones;
|
||||
int vertexCount, jj, kk, ww;
|
||||
float[] weights;
|
||||
Matrix4f m;
|
||||
float weight, ovx, ovy, ovz, onx, ony, onz, vx, vy, vz, nx, ny, nz;
|
||||
for (int ii = 0, bidx = 0; ii < _weightGroups.length; ii++) {
|
||||
vertexCount = _weightGroups[ii].vertexCount;
|
||||
bones = _weightGroups[ii].bones;
|
||||
weights = _weightGroups[ii].weights;
|
||||
for (jj = 0, ww = 0; jj < vertexCount; jj++) {
|
||||
ovx = _ovbuf[bidx];
|
||||
ovy = _ovbuf[bidx + 1];
|
||||
ovz = _ovbuf[bidx + 2];
|
||||
onx = _onbuf[bidx];
|
||||
ony = _onbuf[bidx + 1];
|
||||
onz = _onbuf[bidx + 2];
|
||||
vx = vy = vz = 0f;
|
||||
nx = ny = nz = 0f;
|
||||
for (kk = 0; kk < bones.length; kk++) {
|
||||
m = bones[kk].transform;
|
||||
weight = weights[ww++];
|
||||
|
||||
vx += (ovx*m.m00 + ovy*m.m01 + ovz*m.m02 + m.m03) * weight;
|
||||
vy += (ovx*m.m10 + ovy*m.m11 + ovz*m.m12 + m.m13) * weight;
|
||||
vz += (ovx*m.m20 + ovy*m.m21 + ovz*m.m22 + m.m23) * weight;
|
||||
|
||||
nx += (onx*m.m00 + ony*m.m01 + onz*m.m02) * weight;
|
||||
ny += (onx*m.m10 + ony*m.m11 + onz*m.m12) * weight;
|
||||
nz += (onx*m.m20 + ony*m.m21 + onz*m.m22) * weight;
|
||||
}
|
||||
_vbuf[bidx] = vx;
|
||||
_vbuf[bidx + 1] = vy;
|
||||
_vbuf[bidx + 2] = vz;
|
||||
_nbuf[bidx++] = nx;
|
||||
_nbuf[bidx++] = ny;
|
||||
_nbuf[bidx++] = nz;
|
||||
}
|
||||
}
|
||||
|
||||
// if skinning in real time, copy the data from arrays to buffers;
|
||||
// otherwise, store the mesh as an animation frame
|
||||
if (_storeFrameId == 0) {
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
vbuf.put(_vbuf);
|
||||
nbuf.rewind();
|
||||
nbuf.put(_nbuf);
|
||||
} else {
|
||||
storeFrame();
|
||||
_storeFrameId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current frame data for later use.
|
||||
*/
|
||||
protected void storeFrame ()
|
||||
{
|
||||
if (_storeBlend) {
|
||||
_frames.put(_storeFrameId, new BlendFrame(
|
||||
(float[])_vbuf.clone(), (float[])_nbuf.clone()));
|
||||
} else {
|
||||
TriangleBatch batch = getBatch(0), tbatch = new TriangleBatch();
|
||||
tbatch.setParentGeom(DUMMY_MESH);
|
||||
tbatch.setColorBuffer(batch.getColorBuffer());
|
||||
tbatch.setTextureBuffer(batch.getTextureBuffer(0), 0);
|
||||
tbatch.setIndexBuffer(batch.getIndexBuffer());
|
||||
tbatch.setVertexBuffer(BufferUtils.createFloatBuffer(_vbuf));
|
||||
tbatch.setNormalBuffer(BufferUtils.createFloatBuffer(_nbuf));
|
||||
VBOInfo ovboinfo = batch.getVBOInfo();
|
||||
if (ovboinfo != null) {
|
||||
VBOInfo vboinfo = new VBOInfo(true);
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
|
||||
vboinfo.setVBOTextureID(0, ovboinfo.getVBOTextureID(0));
|
||||
vboinfo.setVBOIndexID(ovboinfo.getVBOIndexID());
|
||||
tbatch.setVBOInfo(vboinfo);
|
||||
} else if (_useDisplayLists) {
|
||||
tbatch.lockMeshes(
|
||||
DisplaySystem.getDisplaySystem().getRenderer());
|
||||
}
|
||||
_frames.put(_storeFrameId, tbatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current vertex and normal buffers for later deformation.
|
||||
*/
|
||||
protected void storeOriginalBuffers ()
|
||||
{
|
||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
||||
vbuf.rewind();
|
||||
nbuf.rewind();
|
||||
FloatBuffer.wrap(_ovbuf = new float[vbuf.capacity()]).put(vbuf);
|
||||
FloatBuffer.wrap(_onbuf = new float[nbuf.capacity()]).put(nbuf);
|
||||
_vbuf = new float[_ovbuf.length];
|
||||
_nbuf = new float[_onbuf.length];
|
||||
}
|
||||
|
||||
/** A stored frame used for linear blending. */
|
||||
protected static class BlendFrame
|
||||
{
|
||||
/** The skinned vertex and normal values. */
|
||||
public float[] vbuf, nbuf;
|
||||
|
||||
public BlendFrame (float[] vbuf, float[] nbuf)
|
||||
{
|
||||
this.vbuf = vbuf;
|
||||
this.nbuf = nbuf;
|
||||
}
|
||||
|
||||
public void blend (
|
||||
BlendFrame next, float alpha, float[] rvbuf, float[] rnbuf)
|
||||
{
|
||||
float[] nvbuf = next.vbuf, nnbuf = next.nbuf;
|
||||
float ialpha = 1f - alpha;
|
||||
for (int ii = 0, nn = vbuf.length; ii < nn; ii++) {
|
||||
rvbuf[ii] = vbuf[ii] * ialpha + nvbuf[ii] * alpha;
|
||||
rnbuf[ii] = nbuf[ii] * ialpha + nnbuf[ii] * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Pre-skinned {@link TriangleBatch}es or {@link BlendFrame}s shared
|
||||
* between all instances corresponding to frame ids from
|
||||
* {@link #storeAnimationFrame}. */
|
||||
protected HashIntMap<Object> _frames;
|
||||
|
||||
/** Whether or to use display lists if VBOs are unavailable for quantized
|
||||
* meshes. */
|
||||
protected boolean _useDisplayLists;
|
||||
|
||||
/** The inverse of the model space reference transform. */
|
||||
protected Matrix4f _invRefTransform;
|
||||
|
||||
/** The groups of vertices influenced by different sets of bones. */
|
||||
protected WeightGroup[] _weightGroups;
|
||||
|
||||
/** The bones referenced by the weight groups. */
|
||||
protected Bone[] _bones;
|
||||
|
||||
/** The original (undeformed) vertex and normal buffers and the deformed
|
||||
* versions. */
|
||||
protected float[] _ovbuf, _onbuf, _vbuf, _nbuf;
|
||||
|
||||
/** The frame id to store on the next update. If 0, don't store any frame
|
||||
* and skin the mesh as normal. If -1, a frame has been stored and thus
|
||||
* skinning should only take place when further frames are requested. */
|
||||
protected int _storeFrameId;
|
||||
|
||||
/** Whether or not the stored frame id will be used for blending. */
|
||||
protected boolean _storeBlend;
|
||||
|
||||
/** A dummy mesh that simply hold transformation values. */
|
||||
protected static final TriMesh DUMMY_MESH = new TriMesh();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.model;
|
||||
|
||||
import com.jme.scene.state.TextureState;
|
||||
|
||||
/**
|
||||
* Provides a means for models to resolve their texture references.
|
||||
*/
|
||||
public interface TextureProvider
|
||||
{
|
||||
/**
|
||||
* Returns a texture state containing the named texture.
|
||||
*
|
||||
* @param name the name of the texture, which will be interpreted as an
|
||||
* absolute resource path if it starts with a forward slash; otherwise,
|
||||
* as a relative resource path
|
||||
*/
|
||||
public TextureState getTexture (String name);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!-- $Id: package.html 617 2001-11-13 00:12:20Z mdb $ -->
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
Defines extensions to <a href="http://jmonkeyengine.com">jMonkeyEngine</a>
|
||||
an OpenGL-based 3D rendering engine. An application framework is
|
||||
provided that integrates jME with the Presents distributed object
|
||||
system and other useful bits and bobs are also included.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* Moves a sprite ballistically.
|
||||
*/
|
||||
public class BallisticPath extends Path
|
||||
{
|
||||
/** Gravity: it's the law. */
|
||||
public static final float G = -9.8f;
|
||||
|
||||
/**
|
||||
* Moves the supplied sprite from the starting coordinate (which will
|
||||
* be modified) using the starting velocity, under the specified
|
||||
* acceleration.
|
||||
*/
|
||||
public BallisticPath (Sprite sprite, Vector3f start, Vector3f velocity,
|
||||
Vector3f accel, float duration)
|
||||
{
|
||||
super(sprite);
|
||||
_curpos = start;
|
||||
_velocity = velocity;
|
||||
_accel = accel;
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
// adjust the position
|
||||
_velocity.mult(time, _temp);
|
||||
_curpos.addLocal(_temp);
|
||||
_sprite.setLocalTranslation(_curpos);
|
||||
|
||||
// check to see if we're done
|
||||
_accum += time;
|
||||
if (_accum >= _duration) {
|
||||
_sprite.pathCompleted();
|
||||
} else {
|
||||
// adjust our velocity due to acceleration
|
||||
_accel.mult(time, _temp);
|
||||
_velocity.addLocal(_temp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the angle of elevation needed to launch a
|
||||
* ballistic projectile at the specified velocity and have it travel
|
||||
* the specified range (when it will once again reach the launch
|
||||
* elevation).
|
||||
*/
|
||||
public static float computeElevation (float range, float vel, float accel)
|
||||
{
|
||||
return FastMath.asin(accel * range / (vel * vel)) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the flight time of a projectile launched at an
|
||||
* angle previously computed with {@link #computeElevation}..
|
||||
*/
|
||||
public static float computeFlightTime (float range, float vel, float angle)
|
||||
{
|
||||
return range / (vel * FastMath.cos(angle));
|
||||
}
|
||||
|
||||
protected Vector3f _curpos, _velocity, _accel;
|
||||
protected float _duration, _accum;
|
||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* Moves a sprite along a straight line.
|
||||
*/
|
||||
public class LinePath extends Path
|
||||
{
|
||||
/**
|
||||
* Moves the supplied sprite from the starting coordinate to the
|
||||
* specified destination coordinate in the specified time.
|
||||
*/
|
||||
public LinePath (Sprite sprite, Vector3f start, Vector3f finish,
|
||||
float duration)
|
||||
{
|
||||
super(sprite);
|
||||
_start = start;
|
||||
_finish = finish;
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
_accum += time;
|
||||
if (_accum >= _duration) {
|
||||
_sprite.setLocalTranslation(_finish);
|
||||
_sprite.pathCompleted();
|
||||
} else {
|
||||
_temp.interpolate(_start, _finish, _accum / _duration);
|
||||
_sprite.setLocalTranslation(_temp);
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3f _start, _finish;
|
||||
protected float _duration, _accum;
|
||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* Moves a sprite along a series of straight lines.
|
||||
*/
|
||||
public class LineSegmentPath extends Path
|
||||
{
|
||||
/**
|
||||
* Creates a path for the supplied sprite traversing the supplied
|
||||
* series of points with the specified duration between points.
|
||||
*
|
||||
* @param points a list of points between which the sprite will be
|
||||
* moved linearly (the sprite will be moved immediately to the first
|
||||
* point).
|
||||
* @param durations defines the elapsed time between each successive
|
||||
* traversal. This will as a result be shorter by one element than the
|
||||
* points array.
|
||||
*/
|
||||
public LineSegmentPath (Sprite sprite, Vector3f up, Vector3f orient,
|
||||
Vector3f[] points, float[] durations)
|
||||
{
|
||||
super(sprite);
|
||||
_up = up;
|
||||
_orient = orient;
|
||||
_points = points;
|
||||
_durations = durations;
|
||||
updateRotation();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
// note the accumulated time
|
||||
_accum += time;
|
||||
|
||||
// if we have surpassed the time for this segment, subtract the
|
||||
// segment time and move on to the next segment
|
||||
while (_current < _durations.length && _accum > _durations[_current]) {
|
||||
_accum -= _durations[_current];
|
||||
advance();
|
||||
}
|
||||
|
||||
// if we have completed our path, move the sprite to the final
|
||||
// position and wrap everything up
|
||||
if (_current >= _durations.length) {
|
||||
_sprite.setLocalTranslation(_points[_points.length-1]);
|
||||
_sprite.pathCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
// move the sprite to the appropriate position between points
|
||||
_temp.interpolate(_points[_current], _points[_current+1],
|
||||
_accum / _durations[_current]);
|
||||
_sprite.setLocalTranslation(_temp);
|
||||
}
|
||||
|
||||
protected void advance ()
|
||||
{
|
||||
_current++;
|
||||
if (_current < _points.length-1) {
|
||||
updateRotation();
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateRotation ()
|
||||
{
|
||||
_points[_current+1].subtract(_points[_current], _temp);
|
||||
PathUtil.computeRotation(_up, _orient, _temp, _rotate);
|
||||
_sprite.getLocalRotation().set(_rotate);
|
||||
}
|
||||
|
||||
protected Vector3f _up, _orient;
|
||||
protected Vector3f[] _points;
|
||||
protected float[] _durations;
|
||||
protected float _accum;
|
||||
protected int _current;
|
||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
||||
protected Quaternion _rotate = new Quaternion();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* A ballistic path that orients the sprite toward the velocity vector as
|
||||
* it traverses the path.
|
||||
*/
|
||||
public class OrientingBallisticPath extends BallisticPath
|
||||
{
|
||||
/**
|
||||
* Creates a {@link BallisticPath} that will rotate the sprite so that
|
||||
* the supplied <code>orient</code> is aligned with the velocity
|
||||
* vector at all points along the path. If the provided orientation is
|
||||
* not initially in line with the starting velocity, the sprite will
|
||||
* be rotated immediately and then adjusted as it follows the path.
|
||||
*/
|
||||
public OrientingBallisticPath (
|
||||
Sprite sprite, Vector3f orient, Vector3f start, Vector3f velocity,
|
||||
Vector3f accel, float duration)
|
||||
{
|
||||
super(sprite, start, velocity, accel, duration);
|
||||
|
||||
// TODO: handle orient that is not (1, 0, 0)
|
||||
_orient = orient;
|
||||
|
||||
// compute the up vector (opposite of acceleration)
|
||||
_up = accel.negate();
|
||||
_up.normalizeLocal();
|
||||
|
||||
_sprite.setLocalRotation(
|
||||
PathUtil.computeAxisRotation(_up, velocity, _rotate));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
// do the normal update
|
||||
super.update(time);
|
||||
|
||||
_sprite.setLocalRotation(
|
||||
PathUtil.computeAxisRotation(_up, _velocity, _rotate));
|
||||
}
|
||||
|
||||
protected Vector3f _orient;
|
||||
protected Vector3f _up;
|
||||
protected Quaternion _rotate = new Quaternion();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.scene.Controller;
|
||||
|
||||
/**
|
||||
* Defines a framework for moving sprites around and notifying interested
|
||||
* parties when the sprite has completed its path or if the path has been
|
||||
* cancelled.
|
||||
*/
|
||||
public abstract class Path extends Controller
|
||||
{
|
||||
/**
|
||||
* Creates and initializes this path with the sprite it will be
|
||||
* manipulating.
|
||||
*/
|
||||
protected Path (Sprite sprite)
|
||||
{
|
||||
_sprite = sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this path is removed from its sprite (either due to
|
||||
* completion or cancellation).
|
||||
*/
|
||||
public void wasRemoved ()
|
||||
{
|
||||
}
|
||||
|
||||
protected Sprite _sprite;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
/**
|
||||
* Implement this interface to find out when a sprite completes or cancels
|
||||
* its path.
|
||||
*/
|
||||
public interface PathObserver extends SpriteObserver
|
||||
{
|
||||
/**
|
||||
* Called when a sprite's path is cancelled either because a new path
|
||||
* was started or the path was explicitly cancelled with {@link
|
||||
* Sprite#cancelMove}.
|
||||
*/
|
||||
public void pathCancelled (Sprite sprite, Path path);
|
||||
|
||||
/**
|
||||
* Called when a sprite completes its traversal of a path.
|
||||
*
|
||||
* @param sprite the sprite that completed its path.
|
||||
* @param path the path that was completed.
|
||||
*/
|
||||
public void pathCompleted (Sprite sprite, Path path);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* Path related utility functions.
|
||||
*/
|
||||
public class PathUtil
|
||||
{
|
||||
/**
|
||||
* Computes a rotation to align the X axis with the specified
|
||||
* orientation and stores it in the provided target. The provided up
|
||||
* vector is crossed with the new orientation to find the left vector
|
||||
* and the left vector is recrossed with the orientation to find the
|
||||
* correct up vector.
|
||||
*
|
||||
* @return the supplied target quaternion.
|
||||
*/
|
||||
public static Quaternion computeAxisRotation (
|
||||
Vector3f up, Vector3f orient, Quaternion target)
|
||||
{
|
||||
_axes[0].set(orient);
|
||||
_axes[0].normalizeLocal();
|
||||
up.cross(_axes[0], _axes[1]);
|
||||
_axes[1].normalizeLocal();
|
||||
_axes[0].cross(_axes[1], _axes[2]);
|
||||
target.fromAxes(_axes);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a rotation from the one vector to another.
|
||||
*
|
||||
* @param axis will be used as the axis of rotation if the two vectors
|
||||
* are parallel.
|
||||
*/
|
||||
public static Quaternion computeRotation (
|
||||
Vector3f axis, Vector3f from, Vector3f to, Quaternion target)
|
||||
{
|
||||
float angle = computeAngle(from, to);
|
||||
if (angle == FastMath.PI) { // opposite
|
||||
target.fromAngleAxis(angle, axis);
|
||||
} else if (angle == 0) { // coincident
|
||||
target.x = target.y = target.z = 0;
|
||||
target.w = 1;
|
||||
} else {
|
||||
from.cross(to, _axis);
|
||||
target.fromAngleAxis(angle, _axis);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the angle between two arbitrary vectors.
|
||||
*/
|
||||
public static float computeAngle (Vector3f one, Vector3f two)
|
||||
{
|
||||
return FastMath.acos(one.dot(two) / (one.length() * two.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the angle between two normalized vectors.
|
||||
*/
|
||||
public static float computeAngleNormal (Vector3f one, Vector3f two)
|
||||
{
|
||||
return FastMath.acos(one.dot(two));
|
||||
}
|
||||
|
||||
protected static Vector3f[] _axes = {
|
||||
new Vector3f(), new Vector3f(), new Vector3f() };
|
||||
protected static Vector3f _axis = new Vector3f();
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Represents a visual entity that one controls as a single unit. Sprites
|
||||
* can be made to follow paths which is one of their primary reasons for
|
||||
* existence.
|
||||
*/
|
||||
public class Sprite extends Node
|
||||
{
|
||||
/**
|
||||
* Walks down the hierarchy provided setting the animation speed on
|
||||
* any controllers found along the way.
|
||||
*/
|
||||
public static void setAnimationSpeed (Spatial spatial, float speed)
|
||||
{
|
||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
||||
spatial.getController(ii).setSpeed(speed);
|
||||
}
|
||||
if (spatial instanceof Node) {
|
||||
Node node = (Node)spatial;
|
||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
||||
setAnimationSpeed(node.getChild(ii), speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down the hierarchy provided turning on or off any controllers
|
||||
* found along the way.
|
||||
*/
|
||||
public static void setAnimationActive (Spatial spatial, boolean active)
|
||||
{
|
||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
||||
spatial.getController(ii).setActive(active);
|
||||
}
|
||||
if (spatial instanceof Node) {
|
||||
Node node = (Node)spatial;
|
||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
||||
setAnimationActive(node.getChild(ii), active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down the hierarchy provided setting the animation repeat type on
|
||||
* any controllers found along the way.
|
||||
*/
|
||||
public static void setAnimationRepeatType (Spatial spatial, int repeatType)
|
||||
{
|
||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
||||
spatial.getController(ii).setRepeatType(repeatType);
|
||||
}
|
||||
if (spatial instanceof Node) {
|
||||
Node node = (Node)spatial;
|
||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
||||
setAnimationRepeatType(node.getChild(ii), repeatType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite ()
|
||||
{
|
||||
super("");
|
||||
setName("sprite:" + hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an observer to this sprite. Observers are notified when path
|
||||
* related events take place.
|
||||
*/
|
||||
public void addObserver (SpriteObserver obs)
|
||||
{
|
||||
if (_observers == null) {
|
||||
_observers = new ObserverList<SpriteObserver>(
|
||||
ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
}
|
||||
_observers.add(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified observer from this sprite.
|
||||
*/
|
||||
public void removeObserver (SpriteObserver obs)
|
||||
{
|
||||
if (_observers != null) {
|
||||
_observers.remove(obs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this sprite is moving along a path, false if not.
|
||||
*/
|
||||
public boolean isMoving ()
|
||||
{
|
||||
return _path != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs this sprite to move along the specified path. Any
|
||||
* currently executing path will be cancelled.
|
||||
*/
|
||||
public void move (Path path)
|
||||
{
|
||||
// if there's a previous path, let it know that it's going away
|
||||
cancelMove();
|
||||
|
||||
// save off this path
|
||||
_path = path;
|
||||
addController(_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any currently executing path. Any registered observers will
|
||||
* be notified of the cancellation.
|
||||
*/
|
||||
public void cancelMove ()
|
||||
{
|
||||
if (_path != null) {
|
||||
Path oldpath = _path;
|
||||
_path = null;
|
||||
oldpath.wasRemoved();
|
||||
if (_observers != null) {
|
||||
_observers.apply(new CancelledOp(this, oldpath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the active path when it has completed. <em>Note:</em>
|
||||
* don't call this method unless you are implementing a {@link Path}.
|
||||
*/
|
||||
public void pathCompleted ()
|
||||
{
|
||||
if (_path == null) {
|
||||
Log.warning("pathCompleted() called on pathless sprite " +
|
||||
"(re-completed?) [sprite=" + this + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
Path oldpath = _path;
|
||||
_path = null;
|
||||
removeController(oldpath);
|
||||
oldpath.wasRemoved();
|
||||
if (_observers != null) {
|
||||
_observers.apply(new CompletedOp(this, oldpath));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the speed of all controllers in our model hierarchy.
|
||||
*/
|
||||
public void setAnimationSpeed (float speed)
|
||||
{
|
||||
setAnimationSpeed(this, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the active state of all controllers in our model hierarchy.
|
||||
*/
|
||||
public void setAnimationActive (boolean active)
|
||||
{
|
||||
setAnimationActive(this, active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the repeat type of all controllers in our model hierarchy.
|
||||
*/
|
||||
public void setAnimationRepeatType (int repeatType)
|
||||
{
|
||||
setAnimationRepeatType(this, repeatType);
|
||||
}
|
||||
|
||||
/** Used to dispatch {@link PathObserver#pathCancelled}. */
|
||||
protected static class CancelledOp
|
||||
implements ObserverList.ObserverOp<SpriteObserver>
|
||||
{
|
||||
public CancelledOp (Sprite sprite, Path path) {
|
||||
_sprite = sprite;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
public boolean apply (SpriteObserver observer) {
|
||||
if (observer instanceof PathObserver) {
|
||||
((PathObserver)observer).pathCancelled(_sprite, _path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Sprite _sprite;
|
||||
protected Path _path;
|
||||
}
|
||||
|
||||
/** Used to dispatch {@link PathObserver#pathCompleted}. */
|
||||
protected static class CompletedOp
|
||||
implements ObserverList.ObserverOp<SpriteObserver>
|
||||
{
|
||||
public CompletedOp (Sprite sprite, Path path) {
|
||||
_sprite = sprite;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
public boolean apply (SpriteObserver observer) {
|
||||
if (observer instanceof PathObserver) {
|
||||
((PathObserver)observer).pathCompleted(_sprite, _path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Sprite _sprite;
|
||||
protected Path _path;
|
||||
}
|
||||
|
||||
protected ObserverList<SpriteObserver> _observers;
|
||||
protected Path _path;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
/**
|
||||
* The super-interface for all sprite observers.
|
||||
*/
|
||||
public interface SpriteObserver
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// $Id: FringeConfiguration.java 3403 2005-03-14 23:58:02Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tile;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Used to manage data about which tiles fringe on which others and how
|
||||
* they fringe.
|
||||
*/
|
||||
public class FringeConfiguration implements Serializable
|
||||
{
|
||||
/** Contains information on a type of tile and all of the fringe
|
||||
* records associated with it. */
|
||||
public static class TileRecord implements Serializable
|
||||
{
|
||||
/** The type of tile to which this applies. */
|
||||
public String type;
|
||||
|
||||
/** The fringe priority of this type. */
|
||||
public int priority;
|
||||
|
||||
/** A list of the fringe records that can be used for fringing. */
|
||||
public ArrayList fringes = new ArrayList();
|
||||
|
||||
/** Used when parsing from an XML definition. */
|
||||
public void addFringe (FringeRecord record)
|
||||
{
|
||||
if (record.isValid()) {
|
||||
fringes.add(record);
|
||||
} else {
|
||||
Log.warning("Not adding invalid fringe record [tile=" + this +
|
||||
", fringe=" + record + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/** Did everything parse well? */
|
||||
public boolean isValid ()
|
||||
{
|
||||
return ((type != null) && (priority > 0));
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[type=" + type + ", priority=" + priority +
|
||||
", fringes=" + StringUtil.toString(fringes) + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** Used to parse the type fringe definitions. */
|
||||
public static class FringeRecord implements Serializable
|
||||
{
|
||||
/** The name of the fringe tileset image. */
|
||||
public String name;
|
||||
|
||||
/** Is this a mask? */
|
||||
public boolean mask;
|
||||
|
||||
/** Did everything parse well? */
|
||||
public boolean isValid ()
|
||||
{
|
||||
return (name != null);
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[name=" + name + ", mask=" + mask + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parsed fringe record to this instance. This is used when
|
||||
* parsing the fringe records from xml.
|
||||
*/
|
||||
public void addTileRecord (TileRecord record)
|
||||
{
|
||||
if (record.isValid()) {
|
||||
_trecs.put(record.type, record);
|
||||
} else {
|
||||
Log.warning("Refusing to add invalid tile record " +
|
||||
"[tile=" + record + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the first type fringes upon the second, return the fringe
|
||||
* priority of the first type, otherwise return -1.
|
||||
*/
|
||||
public int fringesOn (String fringer, String fringed)
|
||||
{
|
||||
TileRecord f1 = (TileRecord)_trecs.get(fringer);
|
||||
|
||||
// we better have a fringe record for the fringer
|
||||
if (null != f1) {
|
||||
// it had better have some types defined
|
||||
if (f1.fringes.size() > 0) {
|
||||
TileRecord f2 = (TileRecord)_trecs.get(fringed);
|
||||
// and we only fringe if fringed doesn't have a fringe
|
||||
// record or has a lower priority
|
||||
if ((null == f2) || (f1.priority > f2.priority)) {
|
||||
return f1.priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random FringeRecord from amongst the ones listed for the
|
||||
* specified type.
|
||||
*/
|
||||
public FringeRecord getFringe (String type, int hashValue)
|
||||
{
|
||||
TileRecord t = (TileRecord)_trecs.get(type);
|
||||
return (FringeRecord)t.fringes.get(hashValue % t.fringes.size());
|
||||
}
|
||||
|
||||
/** The mapping from tile type to tile record. */
|
||||
protected HashMap _trecs = new HashMap();
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
//
|
||||
// $Id: TileFringer.java 3177 2004-10-28 17:49:02Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tile;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.samskivert.util.QuickSort;
|
||||
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Computes fringe tile images according to the rules in an associated
|
||||
* fringe configuration.
|
||||
*/
|
||||
public class TileFringer
|
||||
{
|
||||
public static interface TileSource
|
||||
{
|
||||
/** Returns the type of tile at the specified coordinates or -1 if
|
||||
* there is no tile at this coordinate. */
|
||||
public String getTileType (int x, int y);
|
||||
|
||||
/** Returns the tile type to use when a coordinate has no tile. */
|
||||
public String getDefaultType ();
|
||||
}
|
||||
|
||||
public static interface ImageSource extends ImageUtil.ImageCreator
|
||||
{
|
||||
/** Creates a blank image into which various fringe images will be
|
||||
* composited. */
|
||||
public BufferedImage createImage (
|
||||
int width, int height, int transparency);
|
||||
|
||||
/** Returns the source image for a tile of the specified type.
|
||||
* This can be randomly selected and change from call to call. */
|
||||
public BufferedImage getTileSource (String type);
|
||||
|
||||
/** Returns the named fringe source image (one long strip). */
|
||||
public BufferedImage getFringeSource (String name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fringer that will fringe according to the rules in the
|
||||
* supplied configuration.
|
||||
*/
|
||||
public TileFringer (FringeConfiguration config, ImageSource isrc)
|
||||
{
|
||||
_config = config;
|
||||
_isrc = isrc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes, creates and returns the base tile with the appropriate
|
||||
* fringe imagery applied to it for the specified location.
|
||||
*
|
||||
* @param masks used to cache intermediate images of tiles cut out
|
||||
* using a fringe mask.
|
||||
*/
|
||||
public BufferedImage getFringeTile (
|
||||
TileSource tiles, int col, int row, HashMap masks)
|
||||
{
|
||||
// get the type of the tile we are considering
|
||||
String baseType = tiles.getTileType(col, row);
|
||||
if (baseType == null) {
|
||||
baseType = tiles.getDefaultType();
|
||||
}
|
||||
|
||||
// start with an empty fringer list
|
||||
FringerRec fringers = null;
|
||||
|
||||
// walk through our influence tiles
|
||||
for (int y = row - 1, maxy = row + 2; y < maxy; y++) {
|
||||
for (int x = col - 1, maxx = col + 2; x < maxx; x++) {
|
||||
// we sensibly do not consider ourselves
|
||||
if ((x == col) && (y == row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine the type of our fringing neighbor
|
||||
String fringerType = tiles.getTileType(x, y);
|
||||
if (fringerType == null) {
|
||||
fringerType = tiles.getDefaultType();
|
||||
}
|
||||
|
||||
// determine if it fringes on our tile
|
||||
int pri = _config.fringesOn(fringerType, baseType);
|
||||
if (pri == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FringerRec fringer = (fringers == null) ?
|
||||
null : fringers.find(fringerType);
|
||||
if (fringer == null) {
|
||||
fringer = fringers =
|
||||
new FringerRec(fringerType, pri, fringers);
|
||||
}
|
||||
|
||||
// now turn on the appropriate fringebits
|
||||
int dy = y - row, dx = x - col;
|
||||
fringer.bits |= FLAGMATRIX[dy*3+dx+4];
|
||||
}
|
||||
}
|
||||
|
||||
// if nothing fringed, we're done
|
||||
if (fringers == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// otherwise compose a fringe tile from the specified fringes
|
||||
return composeFringeTile(
|
||||
baseType, fringers.toArray(), masks, TileUtil.getTileHash(col, row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a fringe tile out of the various fringe images needed.
|
||||
*/
|
||||
protected BufferedImage composeFringeTile (
|
||||
String baseType, FringerRec[] fringers, HashMap masks, int hashValue)
|
||||
{
|
||||
// sort the array so that higher priority fringers get drawn first
|
||||
QuickSort.sort(fringers);
|
||||
|
||||
BufferedImage source = _isrc.getTileSource(baseType);
|
||||
if (source == null) {
|
||||
Log.warning("Missing source tile [type=" + baseType + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage ftimg = _isrc.createImage(
|
||||
source.getWidth(), source.getHeight(), Transparency.OPAQUE);
|
||||
Graphics2D gfx = (Graphics2D)ftimg.getGraphics();
|
||||
try {
|
||||
// start with the base tile image
|
||||
gfx.drawImage(source, 0, 0, null);
|
||||
|
||||
// and stamp the fringers on top of it
|
||||
for (int ii = 0; ii < fringers.length; ii++) {
|
||||
int[] indexes = getFringeIndexes(fringers[ii].bits);
|
||||
for (int jj = 0; jj < indexes.length; jj++) {
|
||||
stampTileImage(gfx, fringers[ii].fringerType,
|
||||
indexes[jj], masks, hashValue);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
gfx.dispose();
|
||||
}
|
||||
return ftimg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up or creates the appropriate fringe mask and draws it into
|
||||
* the supplied graphics context.
|
||||
*/
|
||||
protected void stampTileImage (
|
||||
Graphics2D gfx, String fringerType, int index,
|
||||
HashMap masks, int hashValue)
|
||||
{
|
||||
FringeConfiguration.FringeRecord frec =
|
||||
_config.getFringe(fringerType, hashValue);
|
||||
BufferedImage fsimg = (frec == null) ? null :
|
||||
_isrc.getFringeSource(frec.name);
|
||||
if (fsimg == null) {
|
||||
Log.warning("Missing fringe source image [type=" + fringerType +
|
||||
", hash=" + hashValue + ", frec=" + frec + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
if (frec.mask) {
|
||||
// it's a mask; look for it in the cache
|
||||
String maskkey = fringerType + ":" + frec.name + ":" + index;
|
||||
BufferedImage mimg = (BufferedImage)masks.get(maskkey);
|
||||
if (mimg == null) {
|
||||
BufferedImage fsrc = getSubimage(fsimg, index);
|
||||
BufferedImage bsrc = _isrc.getTileSource(fringerType);
|
||||
mimg = ImageUtil.composeMaskedImage(_isrc, fsrc, bsrc);
|
||||
masks.put(maskkey, mimg);
|
||||
}
|
||||
gfx.drawImage(mimg, 0, 0, null);
|
||||
|
||||
} else {
|
||||
// this is a non-mask image so just use the data from the
|
||||
// fringe source image directly
|
||||
gfx.drawImage(getSubimage(fsimg, index), 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>index</code>th tile image from the supplied
|
||||
* source image. The source image is assumed to be a single strip of
|
||||
* tile images, each with equal width and height.
|
||||
*/
|
||||
protected BufferedImage getSubimage (BufferedImage source, int index)
|
||||
{
|
||||
int size = source.getHeight(), x = size * index;
|
||||
return source.getSubimage(x, 0, size, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fringe index specified by the fringebits. If no index
|
||||
* is available, try breaking down the bits into contiguous regions of
|
||||
* bits and look for indexes for those.
|
||||
*/
|
||||
protected int[] getFringeIndexes (int bits)
|
||||
{
|
||||
int index = BITS_TO_INDEX[bits];
|
||||
if (index != -1) {
|
||||
int[] ret = new int[1];
|
||||
ret[0] = index;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// otherwise, split the bits into contiguous components
|
||||
|
||||
// look for a zero and start our first split
|
||||
int start = 0;
|
||||
while ((((1 << start) & bits) != 0) && (start < NUM_FRINGEBITS)) {
|
||||
start++;
|
||||
}
|
||||
|
||||
if (start == NUM_FRINGEBITS) {
|
||||
// we never found an empty fringebit, and since index (above)
|
||||
// was already -1, we have no fringe tile for these bits.. sad.
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
ArrayList indexes = new ArrayList();
|
||||
int weebits = 0;
|
||||
for (int ii=(start + 1) % NUM_FRINGEBITS; ii != start;
|
||||
ii = (ii + 1) % NUM_FRINGEBITS) {
|
||||
|
||||
if (((1 << ii) & bits) != 0) {
|
||||
weebits |= (1 << ii);
|
||||
} else if (weebits != 0) {
|
||||
index = BITS_TO_INDEX[weebits];
|
||||
if (index != -1) {
|
||||
indexes.add(Integer.valueOf(index));
|
||||
}
|
||||
weebits = 0;
|
||||
}
|
||||
}
|
||||
if (weebits != 0) {
|
||||
index = BITS_TO_INDEX[weebits];
|
||||
if (index != -1) {
|
||||
indexes.add(Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
int[] ret = new int[indexes.size()];
|
||||
for (int ii=0; ii < ret.length; ii++) {
|
||||
ret[ii] = ((Integer) indexes.get(ii)).intValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** A record for holding information about a particular fringe as
|
||||
* we're computing what it will look like. */
|
||||
protected static class FringerRec implements Comparable
|
||||
{
|
||||
public String fringerType;
|
||||
public int priority;
|
||||
public int bits;
|
||||
public FringerRec next;
|
||||
|
||||
public FringerRec (String type, int pri, FringerRec next) {
|
||||
fringerType = type;
|
||||
priority = pri;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public FringerRec find (String type)
|
||||
{
|
||||
if (fringerType.equals(type)) {
|
||||
return this;
|
||||
} else if (next != null) {
|
||||
return next.find(type);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public FringerRec[] toArray ()
|
||||
{
|
||||
return toArray(0);
|
||||
}
|
||||
|
||||
public int compareTo (Object o) {
|
||||
return priority - ((FringerRec) o).priority;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return "[type=" + fringerType + ", pri=" + priority +
|
||||
", bits=" + Integer.toString(bits, 16) + "]";
|
||||
}
|
||||
|
||||
protected FringerRec[] toArray (int index)
|
||||
{
|
||||
FringerRec[] array;
|
||||
if (next == null) {
|
||||
array = new FringerRec[index+1];
|
||||
} else {
|
||||
array = next.toArray(index+1);
|
||||
}
|
||||
array[index] = this;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final int NORTH = 1 << 0;
|
||||
protected static final int NORTHEAST = 1 << 1;
|
||||
protected static final int EAST = 1 << 2;
|
||||
protected static final int SOUTHEAST = 1 << 3;
|
||||
protected static final int SOUTH = 1 << 4;
|
||||
protected static final int SOUTHWEST = 1 << 5;
|
||||
protected static final int WEST = 1 << 6;
|
||||
protected static final int NORTHWEST = 1 << 7;
|
||||
|
||||
protected static final int NUM_FRINGEBITS = 8;
|
||||
|
||||
/** A matrix mapping adjacent tiles to which fringe bits they affect.
|
||||
* (x and y are offset by +1, since we can't have -1 as an array index).
|
||||
* These are "upside down" thanks to OpenGL. */
|
||||
protected static final int[] FLAGMATRIX = {
|
||||
SOUTHWEST, (SOUTHEAST | SOUTH | SOUTHWEST), SOUTHEAST,
|
||||
(NORTHWEST | WEST | SOUTHWEST), 0, (NORTHEAST | EAST | SOUTHEAST),
|
||||
NORTHWEST, (NORTHWEST | NORTH | NORTHEAST), NORTHEAST,
|
||||
};
|
||||
|
||||
/** The fringe tiles we use. These are the 17 possible tiles made up
|
||||
* of continuous fringebits sections. */
|
||||
protected static final int[] FRINGETILES = {
|
||||
SOUTHEAST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHWEST,
|
||||
NORTHEAST | EAST | SOUTHEAST,
|
||||
NORTHWEST | WEST | SOUTHWEST,
|
||||
NORTHEAST,
|
||||
NORTHWEST | NORTH | NORTHEAST,
|
||||
NORTHWEST,
|
||||
|
||||
SOUTHWEST | WEST | NORTHWEST | NORTH | NORTHEAST,
|
||||
NORTHWEST | NORTH | NORTHEAST | EAST | SOUTHEAST,
|
||||
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
|
||||
|
||||
NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST,
|
||||
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
|
||||
|
||||
// all the directions!
|
||||
NORTH | NORTHEAST | EAST | SOUTHEAST | SOUTH | SOUTHWEST |
|
||||
WEST | NORTHWEST
|
||||
};
|
||||
|
||||
/** A reverse map of the {@link #FRINGETILES} array, for quickly
|
||||
* looking up which tile we want. */
|
||||
protected static final int[] BITS_TO_INDEX;
|
||||
|
||||
// Construct the BITS_TO_INDEX array.
|
||||
static {
|
||||
int num = (1 << NUM_FRINGEBITS);
|
||||
BITS_TO_INDEX = new int[num];
|
||||
|
||||
// first clear everything to -1 (meaning there is no tile defined)
|
||||
for (int ii=0; ii < num; ii++) {
|
||||
BITS_TO_INDEX[ii] = -1;
|
||||
}
|
||||
|
||||
// then fill in with the defined tiles.
|
||||
for (int ii=0; ii < FRINGETILES.length; ii++) {
|
||||
BITS_TO_INDEX[FRINGETILES[ii]] = ii;
|
||||
}
|
||||
}
|
||||
|
||||
protected ImageSource _isrc;
|
||||
protected FringeConfiguration _config;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// $Id: CompileFringeConfigurationTask.java 3099 2004-08-27 02:21:06Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tile.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.threerings.util.CompiledConfig;
|
||||
|
||||
import com.threerings.jme.tile.tools.xml.FringeConfigurationParser;
|
||||
|
||||
/**
|
||||
* Compiles a fringe configuration from XML format into binary format.
|
||||
*/
|
||||
public class CompileFringeConfigurationTask extends Task
|
||||
{
|
||||
public void setConfig (File config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public void setTarget (File target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
// make sure the config file exists
|
||||
if (!_config.exists()) {
|
||||
throw new BuildException(
|
||||
"Fringe configuration file not found [path=" + _config + "].");
|
||||
}
|
||||
|
||||
FringeConfigurationParser parser = new FringeConfigurationParser();
|
||||
Serializable config;
|
||||
try {
|
||||
config = parser.parseConfig(_config);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure parsing fringe config", e);
|
||||
}
|
||||
|
||||
try {
|
||||
// and write it on out
|
||||
CompiledConfig.saveConfig(_target, config);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure writing serialized config", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected File _config;
|
||||
protected File _target;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// $Id: FringeConfigurationParser.java 3254 2004-11-30 20:03:47Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tile.tools.xml;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
|
||||
import com.threerings.tools.xml.CompiledConfigParser;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
import com.threerings.jme.tile.FringeConfiguration.TileRecord;
|
||||
import com.threerings.jme.tile.FringeConfiguration.FringeRecord;
|
||||
import com.threerings.jme.tile.FringeConfiguration;
|
||||
|
||||
/**
|
||||
* Parses fringe config definitions, which look like so (with angle
|
||||
* brackets instead of square):
|
||||
* <pre>
|
||||
* [fringe]
|
||||
* [tile type="water" priority="100"]
|
||||
* [fringe name="water_fringe_1"/]
|
||||
* [fringe name="water_fringe_2"/]
|
||||
* [fringe name="water_fringe_3"/]
|
||||
* [/tile]
|
||||
* [tile type="dirt" priority="10"]
|
||||
* [fringe name="dirt_fringe_1" mask="true"/]
|
||||
* [/tile]
|
||||
* [tile type="cobble" priority="100"]
|
||||
* [fringe name="cobble_fringe_1" mask="false"/]
|
||||
* [/tile]
|
||||
* [/fringe]
|
||||
* </pre>
|
||||
*/
|
||||
public class FringeConfigurationParser extends CompiledConfigParser
|
||||
{
|
||||
// documentation inherited
|
||||
protected Serializable createConfigObject ()
|
||||
{
|
||||
return new FringeConfiguration();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void addRules (Digester digest)
|
||||
{
|
||||
// configure top-level constraints
|
||||
String prefix = "fringe";
|
||||
digest.addRule(prefix, new SetPropertyFieldsRule());
|
||||
|
||||
// create and configure fringe config instances
|
||||
prefix += "/tile";
|
||||
digest.addObjectCreate(prefix, TileRecord.class.getName());
|
||||
digest.addRule(prefix, new SetPropertyFieldsRule());
|
||||
digest.addSetNext(prefix, "addTileRecord");
|
||||
|
||||
// create the fringe type records in each tile record
|
||||
prefix += "/fringe";
|
||||
digest.addObjectCreate(prefix, FringeRecord.class.getName());
|
||||
digest.addRule(prefix, new SetPropertyFieldsRule());
|
||||
digest.addSetNext(prefix, "addFringe");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
import com.threerings.jme.model.Model;
|
||||
|
||||
/**
|
||||
* A basic representation for keyframe animations.
|
||||
*/
|
||||
public class AnimationDef
|
||||
{
|
||||
/** The rate of the animation in frames per second. */
|
||||
public int frameRate;
|
||||
|
||||
/** A single frame of the animation. */
|
||||
public static class FrameDef
|
||||
{
|
||||
/** Transform for affected nodes. */
|
||||
public ArrayList<TransformDef> transforms =
|
||||
new ArrayList<TransformDef>();
|
||||
|
||||
public void addTransform (TransformDef transform)
|
||||
{
|
||||
transforms.add(transform);
|
||||
}
|
||||
|
||||
/** Adds all transform targets in this frame to the supplied set. */
|
||||
public void addTransformTargets (
|
||||
HashMap<String, Spatial> nodes, HashSet<Spatial> targets)
|
||||
{
|
||||
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
|
||||
String name = transforms.get(ii).name;
|
||||
Spatial target = nodes.get(name);
|
||||
if (target != null) {
|
||||
targets.add(target);
|
||||
} else {
|
||||
Log.debug("Missing animation target [name=" + name +
|
||||
"].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the array of transforms for this frame. */
|
||||
public Model.Transform[] getTransforms (Spatial[] targets)
|
||||
{
|
||||
Model.Transform[] mtransforms =
|
||||
new Model.Transform[targets.length];
|
||||
for (int ii = 0; ii < targets.length; ii++) {
|
||||
mtransforms[ii] = getTransform(targets[ii]);
|
||||
}
|
||||
return mtransforms;
|
||||
}
|
||||
|
||||
/** Returns the transform for the supplied target. */
|
||||
protected Model.Transform getTransform (Spatial target)
|
||||
{
|
||||
String name = target.getName();
|
||||
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
|
||||
TransformDef transform = transforms.get(ii);
|
||||
if (name.equals(transform.name)) {
|
||||
return transform.getTransform();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** A transform for a single node. */
|
||||
public static class TransformDef
|
||||
{
|
||||
/** The name of the affected node. */
|
||||
public String name;
|
||||
|
||||
/** The transformation parameters. */
|
||||
public float[] translation;
|
||||
public float[] rotation;
|
||||
public float[] scale;
|
||||
|
||||
/** Returns the live transform object. */
|
||||
public Model.Transform getTransform ()
|
||||
{
|
||||
return new Model.Transform(
|
||||
new Vector3f(translation[0], translation[1], translation[2]),
|
||||
new Quaternion(rotation[0], rotation[1], rotation[2],
|
||||
rotation[3]),
|
||||
new Vector3f(scale[0], scale[1], scale[2]));
|
||||
}
|
||||
}
|
||||
|
||||
/** The individual frames of the animation. */
|
||||
public ArrayList<FrameDef> frames = new ArrayList<FrameDef>();
|
||||
|
||||
public void addFrame (FrameDef frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "live" animation object that will be serialized with the
|
||||
* object.
|
||||
*
|
||||
* @param props the animation properties
|
||||
* @param nodes the nodes in the model, mapped by name
|
||||
*/
|
||||
public Model.Animation createAnimation (
|
||||
Properties props, HashMap<String, Spatial> nodes)
|
||||
{
|
||||
// find all affected nodes
|
||||
HashSet<Spatial> targets = new HashSet<Spatial>();
|
||||
for (int ii = 0, nn = frames.size(); ii < nn; ii++) {
|
||||
frames.get(ii).addTransformTargets(nodes, targets);
|
||||
}
|
||||
|
||||
// create and configure the animation
|
||||
Model.Animation anim = new Model.Animation();
|
||||
anim.frameRate = frameRate;
|
||||
String rtype = props.getProperty("repeat_type", "clamp");
|
||||
if (rtype.equals("cycle")) {
|
||||
anim.repeatType = Controller.RT_CYCLE;
|
||||
} else if (rtype.equals("wrap")) {
|
||||
anim.repeatType = Controller.RT_WRAP;
|
||||
} else {
|
||||
anim.repeatType = Controller.RT_CLAMP;
|
||||
}
|
||||
|
||||
// collect all transforms
|
||||
anim.transformTargets = targets.toArray(new Spatial[targets.size()]);
|
||||
anim.transforms = new Model.Transform[frames.size()][targets.size()];
|
||||
for (int ii = 0; ii < anim.transforms.length; ii++) {
|
||||
anim.transforms[ii] =
|
||||
frames.get(ii).getTransforms(anim.transformTargets);
|
||||
}
|
||||
return anim;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Vector3f;
|
||||
|
||||
/**
|
||||
* A tool for converting cube maps (sky boxes) to sphere maps.
|
||||
*/
|
||||
public class BuildSphereMap
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 7) {
|
||||
System.err.println("Usage: BuildSphereMap front.ext back.ext " +
|
||||
"left.ext right.ext up.ext dest.ext size");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
execute(new File(args[0]), new File(args[1]), new File(args[2]),
|
||||
new File(args[3]), new File(args[4]), new File(args[5]),
|
||||
Integer.parseInt(args[6]));
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the sphere map.
|
||||
*
|
||||
* @param front the file containing the front side of the cube map
|
||||
* @param back the file containing the back side of the cube map
|
||||
* @param left the file containing the left side of the cube map
|
||||
* @param right the file containing the right side of the cube map
|
||||
* @param up the file containing the up side of the cube map
|
||||
* @param target the file to contain the sphere map
|
||||
* @param size the size of the sphere map to generate
|
||||
*/
|
||||
public static void execute (File front, File back, File left, File right,
|
||||
File up, File target, int size)
|
||||
throws IOException
|
||||
{
|
||||
// load up the sides of the cube map
|
||||
BufferedImage[] sides = new BufferedImage[5];
|
||||
sides[FRONT] = ImageIO.read(front);
|
||||
sides[BACK] = ImageIO.read(back);
|
||||
sides[LEFT] = ImageIO.read(left);
|
||||
sides[RIGHT] = ImageIO.read(right);
|
||||
sides[UP] = ImageIO.read(up);
|
||||
|
||||
// compute the pixels
|
||||
int[] rgb = new int[size * size];
|
||||
Vector3f vec = new Vector3f();
|
||||
for (int y = 0, idx = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++, idx++) {
|
||||
float vx = x / (size*0.5f) - 1f, vy = y / (size*0.5f) - 1f,
|
||||
d2 = vx*vx + vy*vy;
|
||||
int p = 0;
|
||||
if (d2 <= 1f) {
|
||||
vec.set(vx, vy, FastMath.sqrt(1f - d2));
|
||||
rgb[idx] = getCubeMapPixel(vec, sides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create and write the image
|
||||
BufferedImage image = new BufferedImage(size, size,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
image.setRGB(0, 0, size, size, rgb, 0, size);
|
||||
String dest = target.toString(),
|
||||
ext = dest.substring(dest.lastIndexOf('.')+1);
|
||||
ImageIO.write(image, ext, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pixel from the cube map to which the given vector points.
|
||||
*/
|
||||
protected static int getCubeMapPixel (Vector3f vec, BufferedImage[] sides)
|
||||
{
|
||||
int side = getCubeMapSide(vec);
|
||||
|
||||
float s, t;
|
||||
switch (side) {
|
||||
case FRONT:
|
||||
s = -vec.x / vec.y;
|
||||
t = vec.z / vec.y;
|
||||
break;
|
||||
case BACK:
|
||||
s = -vec.x / vec.y;
|
||||
t = -vec.z / vec.y;
|
||||
break;
|
||||
case LEFT:
|
||||
s = vec.y / vec.x;
|
||||
t = -vec.z / vec.x;
|
||||
break;
|
||||
case RIGHT:
|
||||
s = vec.y / vec.x;
|
||||
t = vec.z / vec.x;
|
||||
break;
|
||||
default:
|
||||
case UP:
|
||||
s = vec.x / vec.z;
|
||||
t = -vec.y / vec.z;
|
||||
break;
|
||||
}
|
||||
int width = sides[side].getWidth(), height = sides[side].getHeight();
|
||||
return sides[side].getRGB((int)((width-1) * (s+1f)/2f),
|
||||
(int)((height-1) * (1f-t)/2f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the side index identifying the face of the cube map to which
|
||||
* the given vector points.
|
||||
*/
|
||||
protected static int getCubeMapSide (Vector3f vec)
|
||||
{
|
||||
if (vec.x > vec.z && vec.x > vec.y && vec.x > -vec.y) {
|
||||
return RIGHT;
|
||||
|
||||
} else if (vec.x < -vec.z && vec.x < -vec.y && vec.x < vec.y) {
|
||||
return LEFT;
|
||||
|
||||
} else if (vec.y > vec.z) {
|
||||
return FRONT;
|
||||
|
||||
} else if (vec.y < -vec.z) {
|
||||
return BACK;
|
||||
|
||||
} else {
|
||||
return UP;
|
||||
}
|
||||
}
|
||||
|
||||
/** The sides of the cube map. */
|
||||
protected static final int FRONT = 0, BACK = 1, LEFT = 2, RIGHT = 3,
|
||||
UP = 4;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
/**
|
||||
* An ant task for converting cube maps (sky boxes) to sphere maps.
|
||||
*/
|
||||
public class BuildSphereMapTask extends Task
|
||||
{
|
||||
public void setFront (File front)
|
||||
{
|
||||
_front = front;
|
||||
}
|
||||
|
||||
public void setBack (File back)
|
||||
{
|
||||
_back = back;
|
||||
}
|
||||
|
||||
public void setLeft (File left)
|
||||
{
|
||||
_left = left;
|
||||
}
|
||||
|
||||
public void setRight (File right)
|
||||
{
|
||||
_right = right;
|
||||
}
|
||||
|
||||
public void setUp (File up)
|
||||
{
|
||||
_up = up;
|
||||
}
|
||||
|
||||
public void setTarget (File target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public void setSize (int size)
|
||||
{
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
try {
|
||||
BuildSphereMap.execute(_front, _back, _left, _right, _up, _target,
|
||||
_size);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new BuildException("Failure building sphere map", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** The files representing the sides of the cube map and the target. */
|
||||
protected File _front, _back, _left, _right, _up, _target;
|
||||
|
||||
/** The size of the target image. */
|
||||
protected int _size;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.LoggingSystem;
|
||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
||||
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.model.Model;
|
||||
import com.threerings.jme.model.ModelMesh;
|
||||
import com.threerings.jme.model.ModelNode;
|
||||
import com.threerings.jme.model.SkinMesh;
|
||||
import com.threerings.jme.tools.xml.AnimationParser;
|
||||
import com.threerings.jme.tools.xml.ModelParser;
|
||||
|
||||
/**
|
||||
* An application for compiling 3D models defined in XML to fast-loading binary
|
||||
* files.
|
||||
*/
|
||||
public class CompileModel
|
||||
{
|
||||
/**
|
||||
* Loads the model described by the given properties file and compiles it
|
||||
* to a <code>.dat</code> file in the same directory.
|
||||
*
|
||||
* @return the loaded model, or <code>null</code> if the compiled version
|
||||
* is up-to-date
|
||||
*/
|
||||
public static Model compile (File source)
|
||||
throws Exception
|
||||
{
|
||||
String spath = source.toString();
|
||||
int didx = spath.lastIndexOf('.');
|
||||
String root = (didx == -1) ? spath : spath.substring(0, didx);
|
||||
File content = new File(root + ".mxml"),
|
||||
target = new File(root + ".dat");
|
||||
boolean needsUpdate = false;
|
||||
if (source.lastModified() >= target.lastModified() ||
|
||||
content.lastModified() >= target.lastModified()) {
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
// load the model properties
|
||||
Properties props = new Properties();
|
||||
FileInputStream in = new FileInputStream(source);
|
||||
props.load(in);
|
||||
in.close();
|
||||
|
||||
// locate the animations, if any
|
||||
String[] anims =
|
||||
StringUtil.parseStringArray(props.getProperty("animations", ""));
|
||||
File[] afiles = new File[anims.length];
|
||||
File dir = source.getParentFile();
|
||||
for (int ii = 0; ii < anims.length; ii++) {
|
||||
afiles[ii] = new File(dir, anims[ii] + ".mxml");
|
||||
if (afiles[ii].lastModified() >= target.lastModified()) {
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
if (!needsUpdate) {
|
||||
return null;
|
||||
}
|
||||
System.out.println("Compiling " + source.getParent() + "...");
|
||||
|
||||
// load the model content
|
||||
ModelDef mdef = _mparser.parseModel(content.toString());
|
||||
HashMap<String, Spatial> nodes = new HashMap<String, Spatial>();
|
||||
Model model = mdef.createModel(props, nodes);
|
||||
model.initPrototype();
|
||||
|
||||
// load the animations, if any
|
||||
for (int ii = 0; ii < anims.length; ii++) {
|
||||
System.out.println(" Adding " + afiles[ii] + "...");
|
||||
AnimationDef adef = _aparser.parseAnimation(afiles[ii].toString());
|
||||
model.addAnimation(anims[ii], adef.createAnimation(
|
||||
PropertiesUtil.getSubProperties(props, anims[ii]), nodes));
|
||||
}
|
||||
|
||||
// write and return the model
|
||||
model.writeToFile(target);
|
||||
return model;
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 1) {
|
||||
System.err.println("Usage: CompileModel source.properties");
|
||||
System.exit(-1);
|
||||
}
|
||||
// create a dummy display system
|
||||
new DummyDisplaySystem();
|
||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
||||
|
||||
try {
|
||||
compile(new File(args[0]));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error compiling model: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/** A parser for the model definitions. */
|
||||
protected static ModelParser _mparser = new ModelParser();
|
||||
|
||||
/** A parser for the animation definitions. */
|
||||
protected static AnimationParser _aparser = new AnimationParser();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
||||
import com.jme.util.LoggingSystem;
|
||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
||||
|
||||
/**
|
||||
* An ant task for compiling 3D models defined in XML to fast-loading binary
|
||||
* files.
|
||||
*/
|
||||
public class CompileModelTask extends Task
|
||||
{
|
||||
public void addFileset (FileSet set)
|
||||
{
|
||||
_filesets.add(set);
|
||||
}
|
||||
|
||||
public void init () throws BuildException
|
||||
{
|
||||
// create a dummy display system
|
||||
new DummyDisplaySystem();
|
||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
||||
}
|
||||
|
||||
public void execute ()
|
||||
throws BuildException
|
||||
{
|
||||
for (int ii = 0, nn = _filesets.size(); ii < nn; ii++) {
|
||||
FileSet fs = _filesets.get(ii);
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
File source = new File(fromDir, srcFiles[f]);
|
||||
try {
|
||||
CompileModel.compile(source);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error compiling " + source + ": " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of filesets that contain XML models. */
|
||||
protected ArrayList<FileSet> _filesets = new ArrayList<FileSet>();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import com.jmex.model.XMLparser.Converters.AseToJme;
|
||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
||||
import com.jmex.model.XMLparser.Converters.FormatConverter;
|
||||
import com.jmex.model.XMLparser.Converters.MaxToJme;
|
||||
import com.jmex.model.XMLparser.Converters.Md2ToJme;
|
||||
import com.jmex.model.XMLparser.Converters.Md3ToJme;
|
||||
import com.jmex.model.XMLparser.Converters.ObjToJme;
|
||||
|
||||
/**
|
||||
* A tool for converting various 3D model formats into JME's internal
|
||||
* format.
|
||||
*/
|
||||
public class ConvertModel
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 2) {
|
||||
System.err.println("Usage: ConvertModel source.ext dest.jme");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// create a dummy display system which the converters need
|
||||
new DummyDisplaySystem();
|
||||
|
||||
ConvertModel app = new ConvertModel();
|
||||
File source = new File(args[0]);
|
||||
File target = new File(args[1]);
|
||||
|
||||
String path = source.getPath().toLowerCase();
|
||||
String type = path.substring(path.lastIndexOf(".") + 1);
|
||||
|
||||
// set up our converter
|
||||
FormatConverter convert = null;
|
||||
if (type.equals("obj")) {
|
||||
convert = new ObjToJme();
|
||||
try {
|
||||
convert.setProperty("mtllib", new URL("file:" + source));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to create material URL: " + e);
|
||||
System.exit(-1);
|
||||
}
|
||||
} else if (type.equals("3ds")) {
|
||||
convert = new MaxToJme();
|
||||
} else if (type.equals("md2")) {
|
||||
convert = new Md2ToJme();
|
||||
} else if (type.equals("md3")) {
|
||||
convert = new Md3ToJme();
|
||||
} else if (type.equals("ase")) {
|
||||
convert = new AseToJme();
|
||||
} else {
|
||||
System.err.println("Unknown model type '" + type + "'.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// and do the deed
|
||||
try {
|
||||
BufferedOutputStream bout = new BufferedOutputStream(
|
||||
new FileOutputStream(target));
|
||||
BufferedInputStream bin = new BufferedInputStream(
|
||||
new FileInputStream(source));
|
||||
convert.convert(bin, bout);
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error converting '" + source +
|
||||
"' to '" + target + "'.");
|
||||
ioe.printStackTrace(System.err);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
||||
import com.jme.util.LoggingSystem;
|
||||
import com.jmex.model.XMLparser.Converters.AseToJme;
|
||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
||||
import com.jmex.model.XMLparser.Converters.FormatConverter;
|
||||
import com.jmex.model.XMLparser.Converters.MaxToJme;
|
||||
import com.jmex.model.XMLparser.Converters.Md2ToJme;
|
||||
import com.jmex.model.XMLparser.Converters.Md3ToJme;
|
||||
import com.jmex.model.XMLparser.Converters.ObjToJme;
|
||||
|
||||
/**
|
||||
* An ant task for converting various 3D model formats into JME's internal
|
||||
* format.
|
||||
*/
|
||||
public class ConvertModelTask extends Task
|
||||
{
|
||||
public void addFileset (FileSet set)
|
||||
{
|
||||
_filesets.add(set);
|
||||
}
|
||||
|
||||
public void init () throws BuildException
|
||||
{
|
||||
// create a dummy display system which the converters need
|
||||
new DummyDisplaySystem();
|
||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
||||
}
|
||||
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
for (int i = 0; i < _filesets.size(); i++) {
|
||||
FileSet fs = (FileSet)_filesets.get(i);
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
File cfile = new File(fromDir, srcFiles[f]);
|
||||
String target = srcFiles[f];
|
||||
int didx = target.lastIndexOf(".");
|
||||
target = (didx == -1) ? target : target.substring(0, didx);
|
||||
target += ".jme";
|
||||
convertModel(cfile, new File(fromDir, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void convertModel (File source, File target)
|
||||
{
|
||||
if (source.lastModified() < target.lastModified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Converting " + source + "...");
|
||||
String path = source.getPath().toLowerCase();
|
||||
String type = path.substring(path.lastIndexOf(".") + 1);
|
||||
|
||||
// set up our converter
|
||||
FormatConverter convert = null;
|
||||
if (type.equals("obj")) {
|
||||
convert = new ObjToJme();
|
||||
try {
|
||||
convert.setProperty("mtllib", new URL("file:" + source));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to create material URL: " + e);
|
||||
return;
|
||||
}
|
||||
} else if (type.equals("3ds")) {
|
||||
convert = new MaxToJme();
|
||||
} else if (type.equals("md2")) {
|
||||
convert = new Md2ToJme();
|
||||
} else if (type.equals("md3")) {
|
||||
convert = new Md3ToJme();
|
||||
} else if (type.equals("ase")) {
|
||||
convert = new AseToJme();
|
||||
} else {
|
||||
System.err.println("Unknown model type '" + type + "'.");
|
||||
return;
|
||||
}
|
||||
|
||||
// and do the deed
|
||||
try {
|
||||
BufferedOutputStream bout = new BufferedOutputStream(
|
||||
new FileOutputStream(target));
|
||||
BufferedInputStream bin = new BufferedInputStream(
|
||||
new FileInputStream(source));
|
||||
convert.convert(bin, bout);
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error converting '" + source +
|
||||
"' to '" + target + "'.");
|
||||
ioe.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of filesets that contain tileset bundle definitions. */
|
||||
protected ArrayList _filesets = new ArrayList();
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.tools;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import com.jme.bounding.BoundingBox;
|
||||
import com.jme.bounding.BoundingSphere;
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
import com.threerings.jme.model.Model;
|
||||
import com.threerings.jme.model.ModelController;
|
||||
import com.threerings.jme.model.ModelMesh;
|
||||
import com.threerings.jme.model.ModelNode;
|
||||
import com.threerings.jme.model.SkinMesh;
|
||||
|
||||
/**
|
||||
* An intermediate representation for models used to store data parsed from
|
||||
* XML and convert it into JME nodes.
|
||||
*/
|
||||
public class ModelDef
|
||||
{
|
||||
/** The base class of nodes in the model. */
|
||||
public abstract static class SpatialDef
|
||||
{
|
||||
/** The node's name. */
|
||||
public String name;
|
||||
|
||||
/** The name of the node's parent. */
|
||||
public String parent;
|
||||
|
||||
/** The node's transformation. */
|
||||
public float[] translation;
|
||||
public float[] rotation;
|
||||
public float[] scale;
|
||||
|
||||
/** Returns a JME node for this definition. */
|
||||
public Spatial getSpatial (Properties props)
|
||||
{
|
||||
if (_spatial == null) {
|
||||
_spatial = createSpatial(new NodeProperties(props, name));
|
||||
setTransform();
|
||||
}
|
||||
return _spatial;
|
||||
}
|
||||
|
||||
/** Sets the transform of the created node. */
|
||||
protected void setTransform ()
|
||||
{
|
||||
_spatial.getLocalTranslation().set(translation[0], translation[1],
|
||||
translation[2]);
|
||||
_spatial.getLocalRotation().set(rotation[0], rotation[1],
|
||||
rotation[2], rotation[3]);
|
||||
_spatial.getLocalScale().set(scale[0], scale[1], scale[2]);
|
||||
}
|
||||
|
||||
/** Creates a JME node for this definition. */
|
||||
public abstract Spatial createSpatial (Properties props);
|
||||
|
||||
/** Resolves any name references using the supplied map. */
|
||||
public void resolveReferences (
|
||||
HashMap<String, Spatial> nodes, HashSet<Spatial> referenced)
|
||||
{
|
||||
Spatial pnode = nodes.get(parent);
|
||||
if (pnode instanceof ModelNode) {
|
||||
((ModelNode)pnode).attachChild(_spatial);
|
||||
|
||||
} else if (parent != null) {
|
||||
Log.warning("Missing or invalid parent node [spatial=" +
|
||||
name + ", parent=" + parent + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/** The JME node created for this definition. */
|
||||
protected Spatial _spatial;
|
||||
}
|
||||
|
||||
/** A rigid triangle mesh. */
|
||||
public static class TriMeshDef extends SpatialDef
|
||||
{
|
||||
/** The geometry offset transform. */
|
||||
public float[] offsetTranslation;
|
||||
public float[] offsetRotation;
|
||||
public float[] offsetScale;
|
||||
|
||||
/** Whether or not the mesh allows back face culling. */
|
||||
public boolean solid;
|
||||
|
||||
/** The texture of the mesh, if any. */
|
||||
public String texture;
|
||||
|
||||
/** Whether or not the mesh is (partially) transparent. */
|
||||
public boolean transparent;
|
||||
|
||||
/** The vertices of the mesh. */
|
||||
public ArrayList<Vertex> vertices = new ArrayList<Vertex>();
|
||||
|
||||
/** The triangle indices. */
|
||||
public ArrayList<Integer> indices = new ArrayList<Integer>();
|
||||
|
||||
/** Whether or not any of the vertices have texture coordinates. */
|
||||
public boolean tcoords;
|
||||
|
||||
public void addVertex (Vertex vertex)
|
||||
{
|
||||
int idx = vertices.indexOf(vertex);
|
||||
if (idx != -1) {
|
||||
indices.add(idx);
|
||||
} else {
|
||||
indices.add(vertices.size());
|
||||
vertices.add(vertex);
|
||||
}
|
||||
tcoords = tcoords || vertex.tcoords != null;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Spatial createSpatial (Properties props)
|
||||
{
|
||||
ModelNode node = new ModelNode(name);
|
||||
if (indices.size() > 0) {
|
||||
_mesh = createMesh();
|
||||
configureMesh(props);
|
||||
node.attachChild(_mesh);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Creates the mesh to attach to the node. */
|
||||
protected ModelMesh createMesh ()
|
||||
{
|
||||
return new ModelMesh("mesh");
|
||||
}
|
||||
|
||||
/** Configures the mesh. */
|
||||
protected void configureMesh (Properties props)
|
||||
{
|
||||
// set the geometry offset
|
||||
if (offsetTranslation != null) {
|
||||
_mesh.getLocalTranslation().set(offsetTranslation[0],
|
||||
offsetTranslation[1], offsetTranslation[2]);
|
||||
}
|
||||
if (offsetRotation != null) {
|
||||
_mesh.getLocalRotation().set(offsetRotation[0],
|
||||
offsetRotation[1], offsetRotation[2], offsetRotation[3]);
|
||||
}
|
||||
if (offsetScale != null) {
|
||||
_mesh.getLocalScale().set(offsetScale[0], offsetScale[1],
|
||||
offsetScale[2]);
|
||||
}
|
||||
|
||||
// make sure texture is just a filename
|
||||
int sidx = (texture == null) ? -1 :
|
||||
Math.max(texture.lastIndexOf('/'), texture.lastIndexOf('\\'));
|
||||
if (sidx != -1) {
|
||||
texture = texture.substring(sidx + 1);
|
||||
}
|
||||
|
||||
// configure using properties
|
||||
_mesh.configure(solid, texture, transparent, props);
|
||||
|
||||
// set the various buffers
|
||||
int vsize = vertices.size();
|
||||
ByteOrder no = ByteOrder.nativeOrder();
|
||||
ByteBuffer vbbuf = ByteBuffer.allocateDirect(vsize*3*4).order(no),
|
||||
nbbuf = ByteBuffer.allocateDirect(vsize*3*4).order(no),
|
||||
tbbuf = tcoords ?
|
||||
ByteBuffer.allocateDirect(vsize*2*4).order(no) : null,
|
||||
ibbuf = ByteBuffer.allocateDirect(indices.size()*4).order(no);
|
||||
FloatBuffer vbuf = vbbuf.asFloatBuffer(),
|
||||
nbuf = nbbuf.asFloatBuffer(),
|
||||
tbuf = tcoords ? tbbuf.asFloatBuffer() : null;
|
||||
for (int ii = 0; ii < vsize; ii++) {
|
||||
vertices.get(ii).setInBuffers(vbuf, nbuf, tbuf);
|
||||
}
|
||||
IntBuffer ibuf = ibbuf.asIntBuffer();
|
||||
for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
|
||||
ibuf.put(indices.get(ii));
|
||||
}
|
||||
_mesh.reconstruct(vbbuf, nbbuf, null, tbbuf, ibbuf);
|
||||
|
||||
_mesh.setModelBound("sphere".equals(props.getProperty("bound")) ?
|
||||
new BoundingSphere() : new BoundingBox());
|
||||
_mesh.updateModelBound();
|
||||
|
||||
// set the mesh's origin to the center of its bounding box
|
||||
_mesh.centerVertices();
|
||||
}
|
||||
|
||||
/** The mesh that contains the actual geometry. */
|
||||
protected ModelMesh _mesh;
|
||||
}
|
||||
|
||||
/** A triangle mesh that deforms according to bone positions. */
|
||||
public static class SkinMeshDef extends TriMeshDef
|
||||
{
|
||||
@Override // documentation inherited
|
||||
protected ModelMesh createMesh ()
|
||||
{
|
||||
return new SkinMesh("mesh");
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void resolveReferences (
|
||||
HashMap<String, Spatial> nodes, HashSet<Spatial> referenced)
|
||||
{
|
||||
super.resolveReferences(nodes, referenced);
|
||||
if (_mesh == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create and set the final weight groups
|
||||
SkinMesh.WeightGroup[] wgroups =
|
||||
new SkinMesh.WeightGroup[_groups.size()];
|
||||
HashMap<String, SkinMesh.Bone> bones =
|
||||
new HashMap<String, SkinMesh.Bone>();
|
||||
int ii = 0;
|
||||
for (Map.Entry<Set<String>, WeightGroupDef> entry :
|
||||
_groups.entrySet()) {
|
||||
SkinMesh.WeightGroup wgroup = new SkinMesh.WeightGroup();
|
||||
wgroup.vertexCount = entry.getValue().indices.size();
|
||||
wgroup.bones = new SkinMesh.Bone[entry.getKey().size()];
|
||||
int jj = 0;
|
||||
for (String bname : entry.getKey()) {
|
||||
SkinMesh.Bone bone = bones.get(bname);
|
||||
if (bone == null) {
|
||||
Spatial node = nodes.get(bname);
|
||||
bones.put(bname,
|
||||
bone = new SkinMesh.Bone((ModelNode)node));
|
||||
referenced.add(node);
|
||||
}
|
||||
wgroup.bones[jj++] = bone;
|
||||
}
|
||||
wgroup.weights = toArray(entry.getValue().weights);
|
||||
wgroups[ii++] = wgroup;
|
||||
}
|
||||
((SkinMesh)_mesh).setWeightGroups(wgroups);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
protected void configureMesh (Properties props)
|
||||
{
|
||||
// divide the vertices up by weight groups
|
||||
_groups = new HashMap<Set<String>, WeightGroupDef>();
|
||||
for (int ii = 0, nn = vertices.size(); ii < nn; ii++) {
|
||||
SkinVertex svertex = (SkinVertex)vertices.get(ii);
|
||||
Set<String> bones = svertex.boneWeights.keySet();
|
||||
WeightGroupDef group = _groups.get(bones);
|
||||
if (group == null) {
|
||||
_groups.put(bones, group = new WeightGroupDef());
|
||||
}
|
||||
group.indices.add(ii);
|
||||
for (String bone : bones) {
|
||||
group.weights.add(svertex.boneWeights.get(bone).weight);
|
||||
}
|
||||
}
|
||||
|
||||
// reorder the vertices by group
|
||||
ArrayList<Vertex> overts = vertices;
|
||||
vertices = new ArrayList<Vertex>();
|
||||
int[] imap = new int[overts.size()];
|
||||
for (Map.Entry<Set<String>, WeightGroupDef> entry :
|
||||
_groups.entrySet()) {
|
||||
for (int idx : entry.getValue().indices) {
|
||||
imap[idx] = vertices.size();
|
||||
vertices.add(overts.get(idx));
|
||||
}
|
||||
}
|
||||
for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
|
||||
indices.set(ii, imap[indices.get(ii)]);
|
||||
}
|
||||
|
||||
super.configureMesh(props);
|
||||
}
|
||||
|
||||
/** The intermediate weight groups, mapped by bone names. */
|
||||
protected HashMap<Set<String>, WeightGroupDef> _groups;
|
||||
}
|
||||
|
||||
/** A generic node. */
|
||||
public static class NodeDef extends SpatialDef
|
||||
{
|
||||
// documentation inherited
|
||||
public Spatial createSpatial (Properties props)
|
||||
{
|
||||
return new ModelNode(name);
|
||||
}
|
||||
}
|
||||
|
||||
/** A basic vertex. */
|
||||
public static class Vertex
|
||||
{
|
||||
public float[] location;
|
||||
public float[] normal;
|
||||
public float[] tcoords;
|
||||
|
||||
public void setInBuffers (
|
||||
FloatBuffer vbuf, FloatBuffer nbuf, FloatBuffer tbuf)
|
||||
{
|
||||
vbuf.put(location);
|
||||
nbuf.put(normal);
|
||||
|
||||
if (tbuf != null) {
|
||||
if (tcoords != null) {
|
||||
tbuf.put(tcoords);
|
||||
} else {
|
||||
tbuf.put(0f);
|
||||
tbuf.put(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
Vertex overt = (Vertex)obj;
|
||||
return Arrays.equals(location, overt.location) &&
|
||||
Arrays.equals(normal, overt.normal) &&
|
||||
Arrays.equals(tcoords, overt.tcoords);
|
||||
}
|
||||
}
|
||||
|
||||
/** A vertex influenced by a number of bones. */
|
||||
public static class SkinVertex extends Vertex
|
||||
{
|
||||
/** The bones influencing the vertex, mapped by name. */
|
||||
public HashMap<String, BoneWeight> boneWeights =
|
||||
new HashMap<String, BoneWeight>();
|
||||
|
||||
public void addBoneWeight (BoneWeight weight)
|
||||
{
|
||||
if (weight.weight == 0f) {
|
||||
return;
|
||||
}
|
||||
BoneWeight bweight = boneWeights.get(weight.bone);
|
||||
if (bweight != null) {
|
||||
bweight.weight += weight.weight;
|
||||
} else {
|
||||
boneWeights.put(weight.bone, weight);
|
||||
}
|
||||
}
|
||||
|
||||
/** Finds the bone nodes influencing this vertex. */
|
||||
public HashSet<ModelNode> getBones (HashMap<String, Spatial> nodes)
|
||||
{
|
||||
HashSet<ModelNode> bones = new HashSet<ModelNode>();
|
||||
for (String bone : boneWeights.keySet()) {
|
||||
Spatial node = nodes.get(bone);
|
||||
if (node instanceof ModelNode) {
|
||||
bones.add((ModelNode)node);
|
||||
} else {
|
||||
Log.warning("Missing or invalid bone for bone weight " +
|
||||
"[bone=" + bone + "].");
|
||||
}
|
||||
}
|
||||
return bones;
|
||||
}
|
||||
|
||||
/** Returns the weight of the given bone. */
|
||||
public float getWeight (ModelNode bone)
|
||||
{
|
||||
BoneWeight bweight = boneWeights.get(bone.getName());
|
||||
return (bweight == null) ? 0f : bweight.weight;
|
||||
}
|
||||
}
|
||||
|
||||
/** The influence of a bone on a vertex. */
|
||||
public static class BoneWeight
|
||||
{
|
||||
/** The name of the influencing bone. */
|
||||
public String bone;
|
||||
|
||||
/** The amount of influence. */
|
||||
public float weight;
|
||||
}
|
||||
|
||||
/** A group of vertices influenced by the same bone. */
|
||||
public static class WeightGroupDef
|
||||
{
|
||||
/** The indices of the affected vertex. */
|
||||
public ArrayList<Integer> indices = new ArrayList<Integer>();
|
||||
|
||||
/** The interleaved vertex weights. */
|
||||
public ArrayList<Float> weights = new ArrayList<Float>();
|
||||
}
|
||||
|
||||
/** The meshes and bones comprising the model. */
|
||||
public ArrayList<SpatialDef> spatials = new ArrayList<SpatialDef>();
|
||||
|
||||
public void addSpatial (SpatialDef spatial)
|
||||
{
|
||||
// put nodes before meshes so that bones are updated before skin
|
||||
spatials.add(spatial instanceof NodeDef ? 0 : spatials.size(),
|
||||
spatial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the model node defined herein.
|
||||
*
|
||||
* @param props the properties of the model
|
||||
* @param nodes a node map to populate
|
||||
*/
|
||||
public Model createModel (Properties props, HashMap<String, Spatial> nodes)
|
||||
{
|
||||
Model model = new Model(props.getProperty("name", "model"), props);
|
||||
|
||||
// set the overall scale
|
||||
model.setLocalScale(Float.parseFloat(props.getProperty("scale", "1")));
|
||||
|
||||
// start by creating the spatials and mapping them to their names
|
||||
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
|
||||
Spatial spatial = spatials.get(ii).getSpatial(props);
|
||||
nodes.put(spatial.getName(), spatial);
|
||||
}
|
||||
|
||||
// then go through again, resolving any name references and attaching
|
||||
// root children
|
||||
HashSet<Spatial> referenced = new HashSet<Spatial>();
|
||||
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
|
||||
SpatialDef sdef = spatials.get(ii);
|
||||
sdef.resolveReferences(nodes, referenced);
|
||||
if (sdef.getSpatial(props).getParent() == null) {
|
||||
model.attachChild(sdef.getSpatial(props));
|
||||
}
|
||||
}
|
||||
|
||||
// create any controllers listed
|
||||
String[] controllers = StringUtil.parseStringArray(
|
||||
props.getProperty("controllers", ""));
|
||||
for (int ii = 0; ii < controllers.length; ii++) {
|
||||
Spatial target = nodes.get(controllers[ii]);
|
||||
if (target == null) {
|
||||
Log.warning("Missing controller node [name=" +
|
||||
controllers[ii] + "].");
|
||||
continue;
|
||||
}
|
||||
ModelController ctrl = createController(
|
||||
PropertiesUtil.getSubProperties(props, controllers[ii]),
|
||||
target);
|
||||
if (ctrl != null) {
|
||||
model.addController(ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
// get rid of any nodes that serve no purpose
|
||||
pruneUnusedNodes(model, nodes, referenced);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/** Creates, configures, and returns a model controller. */
|
||||
protected ModelController createController (
|
||||
Properties props, Spatial target)
|
||||
{
|
||||
// attempt to create an instance of the controller
|
||||
ModelController ctrl;
|
||||
String cname = props.getProperty("class", "");
|
||||
try {
|
||||
ctrl = (ModelController)Class.forName(cname).newInstance();
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error instantiating controller [class=" + cname +
|
||||
", error=" + e + "].");
|
||||
return null;
|
||||
}
|
||||
ctrl.configure(props, target);
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
/** Recursively removes any unused nodes. */
|
||||
protected boolean pruneUnusedNodes (
|
||||
ModelNode node, HashMap<String, Spatial> nodes,
|
||||
HashSet<Spatial> referenced)
|
||||
{
|
||||
boolean hasValidChildren = false;
|
||||
for (int ii = node.getQuantity() - 1; ii >= 0; ii--) {
|
||||
Spatial child = node.getChild(ii);
|
||||
if (!(child instanceof ModelNode) ||
|
||||
pruneUnusedNodes((ModelNode)child, nodes, referenced)) {
|
||||
hasValidChildren = true;
|
||||
} else {
|
||||
node.detachChildAt(ii);
|
||||
nodes.remove(child.getName());
|
||||
}
|
||||
}
|
||||
return referenced.contains(node) || hasValidChildren;
|
||||
}
|
||||
|
||||
/** Converts a boxed Integer list to an unboxed int array. */
|
||||
protected static int[] toArray (ArrayList<Integer> list)
|
||||
{
|
||||
int[] array = new int[list.size()];
|
||||
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
|
||||
array[ii] = list.get(ii);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/** Converts a boxed Float list to an unboxed float array. */
|
||||
protected static float[] toArray (ArrayList<Float> list)
|
||||
{
|
||||
float[] array = new float[list.size()];
|
||||
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
|
||||
array[ii] = list.get(ii);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/** A wrapper for the model properties providing access to the properties
|
||||
* of a node within the model. */
|
||||
protected static class NodeProperties extends Properties
|
||||
{
|
||||
public NodeProperties (Properties mprops, String name)
|
||||
{
|
||||
_mprops = mprops;
|
||||
_prefix = name + ".";
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public String getProperty (String key)
|
||||
{
|
||||
return getProperty(key, null);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public String getProperty (String key, String defaultValue)
|
||||
{
|
||||
return _mprops.getProperty(_prefix + key,
|
||||
_mprops.getProperty(key, defaultValue));
|
||||
}
|
||||
|
||||
/** The properties of the model. */
|
||||
protected Properties _mprops;
|
||||
|
||||
/** The node prefix. */
|
||||
protected String _prefix;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user