You are on page 1of 77

1

VRML 2.0 con Java CAPÍTULO 13

Conversion de archivos en
3-D y el uso de Java para
VRML
2

VRML 2.0 con Java CAPÍTULO 13

Contenido CAPÍTULO 12

• Escritura de Convertidores
• WorldToolKit NFF
• Quake MDL
• Animación Master SEG
• Orientación futura

Escritura de transformación

En este capítulo vamos a escribir tres programas de conversión, uno para cada
uno de los tres diferentes formatos de archivo:

• WorldToolKit (formato NFF)


3

VRML 2.0 con Java CAPÍTULO 13

• Quake (formato MDL)


• Máster de Animación (formato SEG)

Elegimos cuidadosamente estos formatos. Ninguno de los tres formatos en la


actualidad el apoyo de los convertidores freeware, después de todo, no hay
ningún punto en la escritura de un convertidor cuando una ya existente. Los
tres formatos también abarcan el ámbito de las aplicaciones 3-D: Quake es un
estado de la técnica 3-D juego; Animación Master es una potente a base de
spline modelado y animación paquete, y es un legado WorldToolKit VR
aplicación. Los tres formatos también nos permiten hacer frente a algunos
desafíos interesantes, entre ellos

• Por el vértice-y por la cara para colorear


• Múltiples texturas por objeto
• Generación de CoordinateInterpolator nodos de animación
• Conversión de formatos de mapa de textura
• Múltiples objetos por archivo
• formatos de archivo binario

Huelga decir que todos los programas de traducción será escrito en Java. Todo
el código fuente para estos convertidores se pueden encontrar en el CD-ROM
que viene con este libro.

Como veremos, uno de los principales retos en la escritura de un buen


traductor es obtener la información necesaria sobre el formato de archivo.
Aunque todos los formatos que nos han de examinar la documentación que
describe, que la documentación que se encuentra en todos los casos,
incompleta, incorrecta, o ambos. Esto plantea un difícil, pero no insuperables,
desafío.

WorldToolKit NFF

WorldToolKit, desde Sense8 Corporation (http://www.sense8.com), fue uno de


los primeros sistemas comerciales VR. Utiliza un sencillo formato de archivo
llamado NFF, que significa neutral Formato de archivo.

Formato de archivo

NFF ficheros de texto ASCII. Están orientadas a la línea, es decir, línea de


fronteras son importantes, y cada uno de los principales datos es el punto en
una línea distinta. Líneas en blanco son permitidos en el archivo, y nada
después de una / / serie en todas las líneas se trata como un comentario que
sigue al final de esa línea.
4

VRML 2.0 con Java CAPÍTULO 13

NFF un archivo tiene la información de la cabecera, seguido de uno o más


objetos. Cada objeto tiene un encabezado, un conjunto de vértices, y un
conjunto de rostros. El archivo de cabecera se compone de una línea que
comienza con NFF, opcionalmente seguida por las líneas que dan el número de
versión del archivo y un punto de vista inicial y la dirección de la vista de
vectores. El objeto de cabecera es simplemente el nombre del objeto, en una
línea por sí mismo. Cabecera que es seguida por un recuento del número de
vértices (de nuevo, en una línea por sí mismo), que a su vez es seguido por los
mismos vértices. Esto es seguido por un recuento del número de caras y la
cara de datos en sí.

En otras palabras, NFF un archivo que contiene dos objetos (uno con 4 vértices
y 7 caras, la otra con 18 vértices y 12 caras) sería algo como esto:

NFF
versión 2.1
viewpos 7 12 56,7
viewdir 0 0 -1
Frodo
4
<vertex data>
7
<face data>
Bilbo
18
<vertex data>
12
<face data>

Vértice de datos

Cada vértice tiene el real x, y, z coordenadas, así como información opcional,


como el color de un vértice, vértice normal, textura o mapa de coordenadas. El
carácter opcional de gran parte de esta información es parte de lo que hace
que leer un archivo NFF algo difícil.

Si el vértice tiene un color, la secuencia rgb 0xRGB aparecerá, cuando RGB es


un número hexadecimal. El primer dígito hexadecimal (es decir, cuatro bits) es
el valor de rojo, el segundo dígito hexadecimal el valor ecológico, y el último
dígito (bajo orden nybble) es el valor azul. Alternativamente, el valor de color
puede ser representado como 0xRRGGBB, en cuyo caso hay 8 bits por
componente de color en lugar de 4.
5

VRML 2.0 con Java CAPÍTULO 13

Si el vértice tiene un vector normal asociado a él, la secuencia norma


aparecerá XYZ, donde XYZ es el vector normal. Si aparece la letra N, con
ninguna norma de información, entonces el vértice debe ser asignado un
vector normal que es el promedio calculado de todos los rostros se enfrentan a
las normales para compartir el vértice.

Si el vértice tiene coordenadas del mapa de textura asociadas con ella, la


secuencia aparecerá UV UV, UV, donde son las coordenadas en el espacio de
texturas. Véase el capítulo 10 para una descripción del mapeado de texturas.

Como ejemplo, aquí hay un vértice que utiliza todas las funciones disponibles:

12,8 76,2 42,17 norma 0,2 0,4 0,7 rgb 0x4A7 uv 0,8 0,65

Cara de datos

Cada cara tiene un recuento del número de puntos en la cara (debe haber al
menos 3 puntos para definir una cara), seguido por el número de enteros que
el índice en el conjunto de vértices. Por ejemplo, 4 2 7 4 5 significa que hay
cuatro puntos a esta cara, y que los vértices son los puntos 2, 7, 4 y 5, en ese
orden. Que debe definirse en un fin de las agujas del reloj, como se ve desde la
parte frontal de la cara.

Después de estos índices es un valor de color. Al igual que en el vértice de


color, el valor de color puede ser un número de 12 bits (0xRGB) o un número
de 24 bits (0xRRGGBB). A raíz de la información de color es un conjunto de
datos opcionales. Una vez más, el carácter facultativo de los datos hace más
difícil de leer un archivo NFF.

Si la línea que describe el rostro contiene la palabra de ambos, entonces la


cara debe ser de doble cara (en cuyo caso, el sentido de las agujas del reloj
orden de los vértices como se ha descrito anteriormente no es pertinente). Si la
línea que contiene la secuencia id = some_number, la cara que se le asigna
valor de referencia para su posterior aplicación por el programa.

Si la línea que contiene la cadena-somename, nombre que se considera otro


mundo a los que se enfrentan es un "portal". Si el usuario hace clic en (o pasa
a través de) este rostro, que serán transportados hasta el mundo (mucho como
un nodo de anclaje en VRML).

Cualquier otra cadena se toma como un mapa de textura nombre. Sin


embargo, los primeros caracteres del nombre de un mapa de textura son
"especiales": si son _S_, la textura debe ser la sombra. Si son _T_, la textura
6

VRML 2.0 con Java CAPÍTULO 13

debe ser transparente. Si son _U_, la textura debe ser transparente y la


sombra. Si son _V_, o cualquier otra cadena, se trata de una "vainilla" textura
que sólo debería ser utilizado como está.

Además, el ángulo de las secuencias de la putrefacción, la escala xy, xy y trans


pueden aparecer en la línea, que se utilizan para rotar, escalar, y traducir el
mapa de textura para esta cara. Espejo de la palabra también puede aparecer,
lo que significa que la textura de mapa ha de ser reflejada.

Tenga en cuenta que la relación entre esta información y la textura de


coordinar la información descrita anteriormente es bastante sutil y hace que el
proceso de traducción más complejas.

Factores que complica

En primer lugar, el proceso de conversión puede parecer sencillo. Después de


todo, los datos sólo consta de vértices y polígonos, ¿por qué no sólo generar un
IndexedFace-Ajuste? Bueno, eso es lo que haremos. Sin embargo, existen
algunos problemas con ese enfoque.

Múltiples objetos por archivo

NFF Un solo archivo puede contener varios objetos, lo que significa que tendrá
que generar IndexedFaceSet múltiples nodos. Este no es un problema, pero
esto significa que vamos a tener varios nodos de forma, cada uno con un
IndexedFaceSet, lo que hace que el archivo VRML un poco detallado.

Múltiples texturas por objeto

VRML 2.0 permite a un único nodo de la forma por la textura, mientras que un
archivo puede tener NFF múltiples texturas de cada objeto. Esta limitación en
VRML significa que tendrá que generar múltiples nodos de la forma de cada
objeto en el archivo-NFF una forma para cada textura, para cada objeto. Si hay
tres objetos en el archivo NFF, cada uno con cinco texturas, nos terminan
generando Forma 15 nodos.

En aras de la eficiencia, nos quiere poner a la lista de los vértices una sola vez
y luego usar una y otra vez, en lugar de poner la lista para cada forma que se
utiliza. Lo mismo puede decirse de las normales, colores, texturas y
coordenadas.
7

VRML 2.0 con Java CAPÍTULO 13

Per-Per-Vertex y cara para colorear

NFF tiene algunas ideas extrañas acerca de cómo determinar la cara de


coloración. Si todos los vértices de referencia por una cara tiene un color
asociado con ellos, los colores deben ser utilizadas y interpolados en la cara
(equivalente a la que se colorPerVertex TRUE en VRML). Si sólo uno de los
vértices de una cara de referencia carece de un color, el color de la cara se
utiliza (lo que equivale a ser colorPerVertex FALSE en VRML). Eso significa que
tenemos que hacer un seguimiento de esa información y utilizarla cuando se
emitan las caras.

Desde VRML no nos permite mezclar y combinar por vértice y por colorear la
cara en un solo IndexedFaceSet, tenemos que poner dos IndexedFaceSet nodos
para cada objeto (con un vértice, por colorido y por una cara con colorante) . Si
cada objeto tiene dos tipos de rostros, nuestra estimación de 15 nodos de la
forma de nuevo el 30 de dobles.

Per-Per-Vertex y Cara normales

Se pone peor. NFF maneja por vértice y cara normales por la misma forma que
los colores, y de nuevo carece de la capacidad de VRML para mezclar por
vértice y cara por las normales en un solo IndexedFaceSet. Ahora estamos
hasta el 60 Formas de NFF un solo archivo.

Coordenadas de textura

Ahora, ninguno de estos problemas es realmente enorme. Acaban de hacer


nuestro código más complejo de escribir, y nuestros archivos de salida algo
mayor. Un problema más grave es la forma de tratar con coordenadas de
textura.

Coordenadas de textura pueden o no estar presentes en el archivo. Si es así,


que debe utilizarse. Si están ausentes, que presumiblemente tienen que ser
generados (como son las normales). Lamentablemente, la documentación
disponible NFF no describe cómo hacerlo. Podríamos entender por ensayo y
error, pero eso es mucho trabajo, por lo que tendrá que hacer algunos
compromisos aquí.

Cabe señalar que, incluso de alta gama comercial de traducción de programas


como InterChange no pretenden apoyar coordenadas de textura en una Sense8
NFF archivo. Evidentemente, es un problema muy complejo.
8

VRML 2.0 con Java CAPÍTULO 13

Un subconjunto razonable

Con un formato tan complejo y barroco como NFF, tenemos que hacer algunas
concesiones. Los compromisos que se va a hacer de la siguiente manera:

• Limitado apoyo a coordenadas de textura


• No hay apoyo para la generación automática vértice normal
• No se refleja el apoyo a las texturas

Si estuviera escribiendo un traductor comercial, que podrían verse tentados a


probar la aplicación de algunas de estas características. Sin embargo, no hay
mucho tutorial de valor que se puede obtener de la escritura más complejo
código en un libro como este. En cualquier caso, el formato es casi NFF ahora
obsoletas; Sense8 el último producto, WorldUp, tiene soporte de VRML pulg

La aplicación de la NFF Parser

Hay dos maneras básicas para escribir un traductor: O bien usted puede
convertir los datos tal como lo lee, o puede analizar el archivo de entrada y
crear un conjunto de estructuras de datos en memoria que se pueda recorrer a
generar el archivo de salida.

El primer enfoque es apropiado cuando dos formatos son muy similares (por
ejemplo, NFF y el formato de GLP que se utiliza para describir los objetos
poligonales en muchas bibliotecas del freeware VR). Sin embargo, el NFF VRML
y formatos son tan diferentes que no tenemos más remedio que analizar todo
el NFF archivo y, a continuación, generar VRML de la memoria en las
estructuras de datos.

Vamos a crear una clase Java para cada uno de los principales tipos de datos se
encuentran en un archivo NFF: Vértice, Cara, Obj, textura, color, y la escena.
Tenga en cuenta que usamos en lugar de Obj objeto de evitar la confusión con
la clase de objetos en Java.

Una escena es básicamente todo un NFF archivo, que consta de todos los
objetos y el punto de vista de la versión más información. El constructor para
cada clase se utilizará para analizar el objeto de esa clase desde el archivo, y la
clase tendrá métodos para acceder a todos los datos pertinentes.

También vamos a crear una pequeña utilidad de clase que es capaz de leer las
líneas de un archivo NFF, saltarse el paso de los comentarios y líneas en
blanco. Creamos una clase aparte, ya que al analizar la funcionalidad de esta
base va a ser necesario por todas las otras clases que se escriba. Otra clase de
9

VRML 2.0 con Java CAPÍTULO 13

servicios públicos, Vec3f, se utilizará para el manejo de vectores en tres


dimensiones.
La clase CommentStripperInputStream

CommentStripperInputStream la clase es una subclase de FilterInputStream.


Aparte de su constructor, sólo implementa un método: readline (), que dispone
de una línea de la matriz DataInputStream, tiras de descuento en todas las
observaciones, y no devuelve ningún líneas vacías:

/ / Comentario de extracción de entrada

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;

clase pública CommentStripperInputStream extiende FilterInputStream {

DataInputStream entrada;

CommentStripperInputStream público (en DataInputStream) {


super (en);
input = en;
}

público final Cadena ReadLine () throws IOException {


Cadena de la línea;
do {
input.readLine línea = ();
si (línea == null)
línea de retorno;
int comment_pos = line.indexOf ("//");
if (comment_pos> = 0)
line.substring línea = (0, comment_pos);
) While (line.length () == 0);
línea de retorno;
}
}

Cabe señalar que la clase StreamTokenizer en el paquete java.io es capaz de


hacer algunos muy inteligente analizar, por ejemplo a despojar / / estilo de
comentarios. Sin embargo, sería más trabajo para utilizar la clase
StreamTokenizer en este caso de lo que es simplemente escribir nuestro propio
10

VRML 2.0 con Java CAPÍTULO 13

código.

La clase Vec3f

Vamos a estar usando vectores tridimensionales mucho en esta aplicación.


Para usarlos, vamos a definir una clase para el tratamiento de Vec3f tales como
un vector de tres valores de punto flotante:

/ / Una simple clase de vectores 3D para analizar efectos NFF

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;
importación java.util .*;

clase pública Vec3f {


protegidas flotar x = 0, y = 0, z = 0;

flotador público getX () (return x;)


flotador público Gety () (return y;)
flotador público Getz () (return z;)

public String toString () {


return new String (x + "" + y + "" + z);
}

público Vec3f (StringTokenizer s)


lanza IOException, NffSyntaxException {
x = Float.valueOf (s.nextToken ()). floatValue ();
y = Float.valueOf (s.nextToken ()). floatValue ();
z = Float.valueOf (s.nextToken ()). floatValue ();
}
}

El constructor dice tres números de punto flotante de un StringTokenizer y las


almacena como la x, y, z y los valores de la Vec3f. El método toString () es útil
cuando queremos imprimir el valor de un Vec3f.
La clase de escena

Escena de la clase almacena la información sobre la escena en sí, tales como el


número de versión del fichero, y se carga desde el punto de vista y ver la
11

VRML 2.0 con Java CAPÍTULO 13

dirección, sino que también mantiene un vector que contiene todos los OBJS
que se encontraban en el archivo. Proporciona métodos para recuperar toda
esa información, incluyendo un recuento del número de objetos.

El constructor analiza el archivo. Se necesita un DataInputStream y construye


un CommentStripperInputStream de él, lee el NFF y verifica la firma y, a
continuación, lee los datos opcionales. Si encuentra un viewpos, viewdir,
versión o declaración, la escena de su objeto actualizaciones internamente los
valores almacenados. Cualquier otra cosa se trata como el inicio de una Obj,
por lo que el Obj constructor se utiliza para analizar la misma. El nuevo objetivo
n º se añade al vector de Obj objetos, analizar y continúa con la siguiente línea
en el archivo de NFF (que suelen ser objeto de otro nombre):

/ / Una clase de escenas NFF

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;
importación java.util .*;

clase pública Escena {


versión protegida flotador = 2.1f;
protegidas Vec3f viewpos, viewdir;
objetos protegidos de vectores = new Vector ();

flotador público getVersion () (return versión;)


público Vec3f getViewpos () (return viewpos;)
público Vec3f getViewdir () (return viewdir;)
público int getNumberOfObjects () (return objects.size ();)
Obj público GetObject (int n) {
return (obj) objects.elementAt (n);
}

Escena público (en DataInputStream)


lanza IOException, NffSyntaxException {
Cadena de la línea;
CommentStripperInputStream entrada = new
CommentStripperInputStream (en);
if (! input.readLine (). startsWith ( "NFF"))
lanzar nuevos NffSyntaxException {
"desaparecidos" NFF "firma");
while ((= línea input.readLine ())! = null) (
12

VRML 2.0 con Java CAPÍTULO 13

StringTokenizer s = new StringTokenizer (linea);


First_token cadena = s.nextToken ();
if (first_token.equals ( "versión"))
version =
Float.valueOf (s.nextToken ()). FloatValue ();
else if (first_token.equals ( "viewpos"))
viewpos = new Vec3f (s);
else if (first_token.equals ( "viewdir"))
viewdir = new Vec3f (s);
else / / nombre de un objeto
objects.addElement (nuevo Obj (first_token, entrada));
}
}
}

Note que nosotros nos encargamos de la viewpos y viewdir utilizando el Vec3f


clase hemos introducido antes.

Obj la clase

NFF un objeto es sólo el nombre de una colección de vértices y caras, por lo


que la clase es muy sencillo:

/ / Una clase de objetos NFF

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;

clase pública Obj {


Cadena nombre protegidas;
int nvertices protegidas, nfaces;
Vértice protegidas [] vértices;
Cara protegidas [] se enfrenta;

public String getName () (return nombre;)


público int getNumberOfVertices () (return nvertices;)
público int getNumberOfFaces () (return nfaces;)
Vértice getVertex público (int n) (return vértices [n];)
Cara getFace público (int n) (return caras [n];)
13

VRML 2.0 con Java CAPÍTULO 13

Obj público (String namevalue, CommentStripperInputStream entrada)


lanza IOException, NffSyntaxException {
nombre = new String (namevalue);
vértices = new Vértice [nvertices =
Integer.parseInt (input.readLine ())];
for (int i = 0; i <nvertices; + + i)
vértices [i] = new Vértice (entrada);
caras nuevas de cara = [nfaces =
Integer.parseInt (input.readLine ())];
for (int i = 0; i <nfaces; + + i) {
se enfrenta a [i] = new Face (entrada);
for (int j = 0; j <caras [i]. getNumberOfPoints (); j + +) {
V = vértice vértices [caras [i]. GetPoint (j)];
if (v.getColor () == null)
/ / Al menos uno no tiene color
se enfrenta a [i]. useVertexColors = false;
if (v.getNormal () == null)
/ / Por lo menos no tiene una normal
se enfrenta a [i]. useVertexNormals = false;
}
}
}
}

Una vez más, el verdadero trabajo se hace en el método constructor. El


constructor almacena el nombre y luego lee el vértice contar. A continuación,
asigna un array con espacio para que el número de vértices y el Vertex clase
utiliza para leer los vértices sí mismos.

Lo mismo se hace con las caras, pero también hacer un poco de post-
procesamiento. Vamos a través de los vértices que forman la cara, la
comprobación para ver si alguno de ellos ha desaparecido o su color normal. Si
alguno de ellos es, nos ponemos una bandera que vamos a comprobar más
tarde cuando se exporta a VRML.
El Vertex Clase

Un vértice tiene un Vec3f de su posición en un espacio tridimensional. También


tiene una Vec3f por su vector normal, que será nula si no hay ninguno. Vamos a
seguir la pista de si el vector debe tener un defecto normal (N en el vértice de
la línea que hemos descrito anteriormente). Un vértice tiene un color, que a su
vez puede ser nulo, y un par de coordenadas de textura:

/ / Una clase de NFF vértices


14

VRML 2.0 con Java CAPÍTULO 13

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;
importación java.util .*;

clase pública Vértice {


Vec3f posición protegida;
protegidas Vec3f normal;
protegidas use_default_normal boolean = false;
Color protegidas;
Flotador protegidas u, v;
protegidas has_texture boolean = false;

público Vec3f getPosition () (return posición;)


público Vec3f getNormal () (return normal;)
flotador público getU () (return (u == null)? 0: u.floatValue ();)
flotador público getV () (return (v == null)? 0: v.floatValue ();)
Color getColor público () (return color;)
público hasTexture boolean () (return has_texture;)
público useDefaultNormal boolean () (return use_default_normal;)

Vértice público (CommentStripperInputStream entrada)


lanza IOException, NffSyntaxException {
Cadena = input.readLine línea ();
StringTokenizer s = new StringTokenizer (linea);
posición = new Vec3f (s);
while (s.hasMoreTokens ()) {
Token = cadena s.nextToken ();
if (token.equals ( "norma"))
normal = new Vec3f (s);
else if (token.equals ( "N"))
use_default_normal = true;
else if (token.equals ( "rgb"))
color = new Color (s.nextToken ());
else if (token.equals ( "uv")) (
u = nuevo flotador (s.nextToken ());
v = new flotador (s.nextToken ());
has_texture = true;
}
}
}
}
15

VRML 2.0 con Java CAPÍTULO 13

El constructor de clase para el Vertex dice una sola línea de la


CommentStripperInputStream y lo analiza con un StringTokenizer. Observe que
usamos el color de clase para analizar la actual especificación de color.

Clase de color

Un color es sólo un conjunto de rojo, verde y azul. El constructor lo analiza a


partir de una cadena hexadecimal, con el prefijo 0x:

/ / Color para un medio ambiente Sense8

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

clase pública Color {


público int rojo, verde, azul;

public String toString () {


volver red/255f + "" + green/255f + "" + blue/255f;
}

Color público () (rojo = verde = azul = 0;)

Color público (String s) {


int color = Integer.parseInt (s.substring (2), 16);
if (s.length () <= 5) (/ / valor de 12 bits
= rojo (color>> 4) & 0xF0;
y de color verde = 0xF0;
azul = (color <<4) & 0xF0;
}
else {
= rojo (color>> 16) & 0xFF;
verde = (color>> 8) & 0xFF;
y de color azul = 0xFF;
}
}
}

Si la longitud de la cadena es <= 5, entonces sólo se ha nybbles tres (dígitos


hexadecimales), por lo que es de 12 bits de color. En caso contrario, tenemos a
16

VRML 2.0 con Java CAPÍTULO 13

color de 24 bits. En cualquier caso, los bits que cambio la cuantía apropiada en
la correcta dirección y la máscara ellos.

El Rostro de clase

El Rostro clase de obras de la misma forma que hace Vértice. Se realiza un


seguimiento del número de puntos en la cara, los mismos puntos, se enfrentan
a un color, un tanto del pabellón, un número de identificación, nombre de un
portal, y la textura de información:

/ / Una clase de NFF caras

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

importación java.io. *;
importación java.util .*;

clase pública Cara {


int npoints protegidas; / / número de puntos en la cara
protegidas int [] puntos; / / conjunto de índices en los vértices
Color color protegidas; / / color de la cara
protegidas tanto boolean = false; / / la cara es visible desde ambos lados
protegidas int id; / / identificador para el rostro
textura Textura protegidas; / / textura mapa
Cadena portal protegidas; / / portal de esta cara
boolean useVertexNormals público; / / vértice uso normales
boolean useVertexColors público; / / uso vértice colores

público int getNumberOfPoints () (return npoints;)


público int getPoint (int n) (return puntos [n];)
público isDoublesided boolean () (return dos;)
Textura getTexture público () (return textura;)
Color getColor público () (return color;)
público int getId () (return id;)

Cara público (CommentStripperInputStream entrada)


lanza IOException, NffSyntaxException {
Cadena = input.readLine línea ();
StringTokenizer s = new StringTokenizer (linea);
puntos = new int [npoints =
Integer.parseInt (s.nextToken ())];
17

VRML 2.0 con Java CAPÍTULO 13

for (int i = 0; i <npoints; + + i)


puntos [i] = Integer.parseInt (s.nextToken ());
if (s.hasMoreTokens ()) / / color
color = new Color (s.nextToken ());
while (s.hasMoreTokens ()) (
Cmd = s.nextToken cadena ();
if (cmd.equals ( "dos"))
ambos = true;
else if (cmd.startsWith ( "id"))
id = Integer.parseInt (cmd.substring (3));
else if (cmd.equals ( "podredumbre"))
texture.setRotation {
Float.valueOf (s.nextToken ()). FloatValue ());
else if (cmd.equals ( "escala"))
texture.setScale {
Float.valueOf (s.nextToken ()). FloatValue ());
else if (cmd.equals ( "espejo"))
texture.setMirror (true);
else if (cmd.equals ( "trans")) {
flotador sval =
Float.valueOf (s.nextToken ()). FloatValue ();
flotador tval =
Float.valueOf (s.nextToken ()). FloatValue ();
texture.setTrans (sval, tval);
}
else if (cmd.charAt (0) == '-')
portal = new String (cmd.substring (1));
algo más
textura Textura = new (cmd);
}
useVertexNormals = true;
useVertexColors = true;
}
}

Como Vértice, de cara dice una sola línea de un CommentStripperInputStream.


Se pone el número de puntos en la cara y que analiza el número de vértices,
los índices de matrices. Asimismo, Vértice, utiliza el color de cara a analizar la
clase de especificación de color.

Analizar un rostro también implica la comprobación de las distintas palabras


clave opcional (como tanto, id, y espejo de aumento), así como recoger la
textura utilizando el nombre y la textura de clase para analizar la misma.
18

VRML 2.0 con Java CAPÍTULO 13

La Clase de textura

Asociados a cada textura, tenemos un nombre de archivo (que hace referencia


al archivo que contiene la imagen de mapa de bits de la textura), algunos se
transforma (como rotación, escalado, traducción y reflexión), y las banderas
para indicar si la textura es la sombra y / o texturados. La clase de textura se
muestra a continuación:

/ / Descripción de una textura Sense8 NFF archivo

/ / Escrito por Bernie Roehl, noviembre de 1996

NFF paquete;

clase pública de textura {


Cadena de nombre de archivo protegidas; / / textura del mapa a utilizar
protegidas flotar putrefacción 0F = / / textura de rotación en radianes
protegidas flotar escala = 1 septies / / textura factor de escala
protegidas boolean espejo = false; / / textura debe ser reflejado
protegidas sTrans flotador = 0F; / / S textura traducción
protegidas tTrans flotador = 0F; / / T textura traducción
sombreada protegida boolean = false; / / textura de la sombra
transp protegidas boolean = false; / / textura de la sombra

public String toString () {


volver filename + "sombra =" + + sombra "transp =" + transp
+ "Espejo =" + + espejo "putrefacción =" + putrefacción
+ "Scale =" + escala
+ "Trans =" sTrans + + "," + tTrans;
}

getFilename public String () (return filename;)


flotador público getRotation () (return putrefacción;)
flotador público getScale () (return escala;)
público getMirror boolean () (return espejo;)
flotador público getTransS () (return sTrans;)
flotador getTransT público () (return tTrans;)
public void setRotation (float r) (r = putrefacción;)
public void setScale (float s) (escala = s;)
public void setMirror (boolean m) = m (espejo;)
public void settrans (float s, float t) {
sTrans = s; tTrans = t;
}
público isShaded boolean () (return sombra;)
19

VRML 2.0 con Java CAPÍTULO 13

público isTransparent boolean () (return transp;)

Textura público (String tex) {


filename = new String (tex);
if (filename.startsWith ( "_v_")
| | Filename.startsWith ( "_V_")) {
filename = filename.substring (3);
}
if (filename.startsWith ( "_s_")
| | Filename.startsWith ( "_S_")) {
filename = filename.substring (3);
sombreada = true;
}
if (filename.startsWith ( "_t_")
| | Filename.startsWith ( "_T_")) {
filename = filename.substring (3);
transp = true;
}
if (filename.startsWith ( "_u_")
| | Filename.startsWith ( "_U_")) {
filename = filename.substring (3);
sombreada = true;
transp = true;
}
}
público boolean partido (Textura tex2) {
if (tex2 == null)
return false;
if (! filename.equals (tex2.filename))
return false;
if (putrefacción! = tex2.rot | | escala! = tex2.scale
| | Espejo! Tex2.mirror =)
return false;
if (sTrans! = tex2.sTrans | | tTrans! = tex2.tTrans)
return false;
return true;
}
}

Desde la escala, la rotación, la traducción, el reflejo y se analiza en el


constructor de la cara, no necesita preocuparse mucho acerca de ellos aquí
(excepto el suministro de métodos para ajustar los valores). Analizar el nombre
del archivo de los rendimientos de la sombra y la transparencia banderas.
20

VRML 2.0 con Java CAPÍTULO 13

El partido () sea el método utilizado para comparar dos texturas para la


igualdad. Si todos los campos coinciden, se devuelve true. Usaremos esta
función cuando es el momento de generar el VRML.

Generación de la salida de VRML

En este punto, podemos cargar un archivo en la memoria NFF muy fácilmente:

Flujo de entrada d = new FileInputStream (filename);


Escena Escena escena = new (nuevo DataInputStream (d));

La vida no tiene mucho más fácil que eso, ¿verdad?

Por supuesto, ahora las cosas se ponen difíciles. Tenemos que tomar ese
conjunto de estructuras de datos que Acabamos de construida y convertirla en
VRML. El algoritmo básico es el siguiente:

para cada objeto en la escena


para cada textura única en el objeto
para cada combinación de por la cara y por vértice normales y colores
poner una forma

El método main () de nuestra clase nff2vrml carga la escena y pide


dump_scene () para escribir en VRML:

/ / Analizar Sense8 NFF un archivo y convertirlo a formato VRML 2.0

/ / Escrito por Bernie Roehl, noviembre de 1996

importación java.io. *;
importación java.util .*;
importación NFF .*;
clase pública nff2vrml {
public static void main (String args []) {
intentar {
D flujo de entrada;
if (args.length == 1)
d = new FileInputStream (args [0]);
algo más
d = System.in;
Escena Escena escena = new (nuevo DataInputStream (d));
dump_scene (escena);
21

VRML 2.0 con Java CAPÍTULO 13

}
catch (IOException e) (System.err.println (e);)
de capturas (NffSyntaxException e) (System.err.println (e);)
}

El dump_scene () Método

El dump_scene () método es bastante sencillo:

static void dump_scene (Escena escena) {


System.out.println ( "# VRML V2.0 utf8");
System.out.println ( "\ n # creación de nff2vrml \ n");
for (int i = 0; i <scene.getNumberOfObjects (); + + i)
dump_object (scene.getObject (i));
}

Sólo coloca el nivel de cabecera VRML 2.0, añade un comentario para


identificar el archivo que ha sido generado automáticamente por un traductor,
y luego le dice a cada uno de los objetos para volcar en formato VRML.

El dump_object () Método

Un objeto de dumping no es mucho más difícil. Hay una importante


optimización que tenemos que hacer, sin embargo: No queremos que emiten
vértices de la misma más de una vez, ni la misma normales, ni los mismos
colores, ni ningún otro "voluminosos" de datos. En lugar de ello, queremos
hacer un seguimiento del hecho de que ya hemos vertido una vez y, a
continuación, la USO ellos después. Utilizamos un conjunto de banderas
booleano para ello:

estático privado dumped_coordinates boolean;


estático privado dumped_vertex_normals boolean;
estático privado dumped_vertex_colors boolean;
estático privado dumped_face_colors boolean;
estático privado dumped_texture_coordinates boolean;
estático privado emit_texture_coordinates boolean = false;

static void dump_object (objeto obj) {


System.out.println ( "DEF" object.getName + () + "Grupo (");
System.out.println ( "\ tchildren [");
Texturas vector = new Vector ();
boolean have_untextured_faces = false;
for (int i = 0; i <object.getNumberOfFaces (); + + i) {
22

VRML 2.0 con Java CAPÍTULO 13

Textura Textura = object.getFace (i). GetTexture ();


if (textura! = null) (
if (! contains_texture (texturas, textura))
textures.addElement (textura);
}
algo más
have_untextured_faces = true;
}
dumped_coordinates = dumped_vertex_normals = false;
dumped_vertex_colors = dumped_face_colors = false;
dumped_texture_coordinates = false;
La enumeración e = textures.elements ();
while (e.hasMoreElements ()) / / volcado de un objeto para cada textura
dump_textured_object (objeto, (textura) e.nextElement ());
if (have_untextured_faces)
dump_textured_object (objeto, null); / / untextured caras
System.out.println ( "\ t]");
System.out.println ("}");
}

Creamos un Grupo de nodo, que incluyen todos los nodos de forma estaremos
generando para este objeto. A continuación, a través de todos los rostros en el
objeto, la creación de una lista de las diferentes texturas nos encontramos.
Esta lista se almacena en un vector llamado texturas. También realizar un
seguimiento de si hay o no untextured caras.

Estamos inicializar todas las banderas dumped_ un falso, a continuación,


EJECUTE a través de los vectores de volcado de texturas y los objetos que se
enfrenta a que el uso de texturas. En otras palabras, generar un objeto de
textura (una forma que contiene un nodo IndexedFaceSet, para ser técnico)
para cada textura que tiene este objeto. Si hay alguna untextured caras, que
son emitidos en último lugar.

El contains_texture () la función es bastante simple y utiliza el Texture.match ()


el método que hemos debatido antes:

boolean contains_texture estático (texture_list de vectores, de textura de


textura) {
La enumeración e = texture_list.elements ();
while (e.hasMoreElements ()) (/ / ver si tenemos presente una
T = textura (textura) e.nextElement ();
if (t! = null)
if (t.match (textura)) / / encontrado una coincidencia!
return true;
23

VRML 2.0 con Java CAPÍTULO 13

}
return false;
}

Cualquier persona que no sea nula textura que pasa el partido () devuelve
verdadera prueba, todo lo devuelve falso.

El dump_textured_object () Método

Textura para cada objeto que un vertedero, hay cuatro posibles sub-objetos:

• Una de las normales por cara y cara por los colores


• Uno por cada cara con las normales, pero por vértice colores
• Uno de cada vértice normales, pero por la cara los colores
• Uno de cada vértice y normales por vértice colores.

Vamos a generar una forma de nodo para cada uno, poniendo a un nodo que
haga referencia a la apariencia de la textura mapa.

Dentro de cada uno de forma que generará un IndexedFaceSet, con la


configuración correcta para normalPerVertex y colorPerVertex. La lista de
colores que ponemos a cabo también por la cara o por vértice, según sea
necesario (utilizando dump_vertex_colors () o dump_face_colors () según sea el
caso), y, cuando sea necesario vamos a llamar dump_vertex_normals () para
apagar el normal vectores ellos mismos. El código siguiente muestra cómo
funciona:

static void dump_textured_object (Obj objeto, de textura de textura) {


if (hasAny (objeto, falso, falso)) {
/ / Vértice no normales o colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex FALSE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex FALSE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_faces (objeto, la textura, falsa, false);
dump_face_colors (objeto, la textura, false);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
24

VRML 2.0 con Java CAPÍTULO 13

if (hasAny (objeto, falso, verdadero)) {


/ / Colores de vértices, pero no las normales
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex FALSE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex TRUE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_colors (objeto);
dump_faces (objeto, la textura, falso, verdadero);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
if (hasAny (objeto, verdadero, falso)) {
/ / Vértice normales, pero no los colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex TRUE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex FALSE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_normals (objeto);
dump_face_colors (objeto, la textura, true);
dump_faces (objeto, la textura, el verdadero, false);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
if (hasAny (objeto, true, true)) {
/ / Vértice tanto normales y colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex TRUE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex TRUE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_colors (objeto);
dump_vertex_normals (objeto);
dump_faces (objeto, la textura, true, true);
25

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet


System.out.println ( "\ t \ t)"); / / Forma final
}
}

El has_any () el método se usa para ver si hay algo para arrojar por una
combinación, si no hay, no se nada de volcado:

boolean hasAny estático (Obj objeto, vnormals boolean, boolean vcolors) {


for (int i = 0; i <object.getNumberOfFaces (); + + i) {
F = object.getFace cara (i);
if (f.useVertexNormals == vnormals
& & F.useVertexColors == vcolors)
return true;
}
return false;
}
El dump_textured_object () Método

Textura para cada objeto que un vertedero, hay cuatro posibles sub-objetos:

• Una de las normales por cara y cara por los colores


• Uno por cada cara con las normales, pero por vértice colores
• Uno de cada vértice normales, pero por la cara los colores
• Uno de cada vértice y normales por vértice colores.

Vamos a generar una forma de nodo para cada uno, poniendo a un nodo que
haga referencia a la apariencia de la textura mapa.

Dentro de cada uno de forma que generará un IndexedFaceSet, con la


configuración correcta para normalPerVertex y colorPerVertex. La lista de
colores que ponemos a cabo también por la cara o por vértice, según sea
necesario (utilizando dump_vertex_colors () o dump_face_colors () según sea el
caso), y, cuando sea necesario vamos a llamar dump_vertex_normals () para
apagar el normal vectores ellos mismos. El código siguiente muestra cómo
funciona:

static void dump_textured_object (Obj objeto, de textura de textura) {


if (hasAny (objeto, falso, falso)) {
/ / Vértice no normales o colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex FALSE");
26

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ t \ tcolorPerVertex FALSE");


dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_faces (objeto, la textura, falsa, false);
dump_face_colors (objeto, la textura, false);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
if (hasAny (objeto, falso, verdadero)) {
/ / Colores de vértices, pero no las normales
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex FALSE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex TRUE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_colors (objeto);
dump_faces (objeto, la textura, falso, verdadero);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
if (hasAny (objeto, verdadero, falso)) {
/ / Vértice normales, pero no los colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");
System.out.println ( "\ t \ t \ t \ tnormalPerVertex TRUE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex FALSE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_normals (objeto);
dump_face_colors (objeto, la textura, true);
dump_faces (objeto, la textura, el verdadero, false);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
if (hasAny (objeto, true, true)) {
/ / Vértice tanto normales y colores
System.out.println ( "\ t \ (tShape");
dump_appearance (textura);
27

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ tgeometry IndexedFaceSet (");


System.out.println ( "\ t \ t \ t \ tnormalPerVertex TRUE");
System.out.println ( "\ t \ t \ t \ tcolorPerVertex TRUE");
dump_coordinates (objeto);
if (textura! = null)
dump_texture_coordinates (objeto);
dump_vertex_colors (objeto);
dump_vertex_normals (objeto);
dump_faces (objeto, la textura, true, true);
System.out.println ( "\ t \ t \ t)"); / / fin IndexedFaceSet
System.out.println ( "\ t \ t)"); / / Forma final
}
}

El has_any () el método se usa para ver si hay algo para arrojar por una
combinación, si no hay, no se nada de volcado:

boolean hasAny estático (Obj objeto, vnormals boolean, boolean vcolors) {


for (int i = 0; i <object.getNumberOfFaces (); + + i) {
F = object.getFace cara (i);
if (f.useVertexNormals == vnormals
& & F.useVertexColors == vcolors)
return true;
}
return false;
}

El dump_ Métodos

El resto de nff2vrml consiste principalmente de los métodos de arrays de


dumping de vértices, colores, caras, etc. Echemos un vistazo a un par de ellos
para ver cómo funcionan:

static void dump_coordinates (objeto obj) {


if (dumped_coordinates == true)
System.out.println ( "\ t \ t \ t \ tcoord USO C");
else {
System.out.println ( "\ t \ t \ t \ tcoord DEF Coordinar (C");
System.out.println ( "\ t \ t \ t \ t \ tpoint [");
for (int i = 0; i <object.getNumberOfVertices (); + + i)
System.out.println ( "\ t \ t \ t \ t \ t \ t" +
object.getVertex (i). getPosition ());
System.out.println ( "\ t \ t \ t \ t \ t]");
28

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ t \ t)");


dumped_coordinates = true;
}
}

Esta rutina vuelca el conjunto de los vértices de coordenadas. El primer paso es


comprobar si la serie ya ha sido objeto de dumping, en caso afirmativo, que
acabamos de utilizar. De lo contrario, DEF para su uso posterior, y poner los
vértices. A continuación, establecemos la bandera a dumped_coordinates
cierto.

Dumping vértice normales es similar, salvo que se compruebe si existe un


vector normal de un vértice y emiten 0 0 0 si no lo hace. El mismo se realiza
para el dumping vértice colores.

Dumping se enfrenta es ligeramente diferente. Sólo queremos volcar esos


rostros que coincidan con la combinación de per-vertex/per-face normales y
per-vertex/per-face colores que la transformación que estamos:

static void dump_faces (Obj objeto, de textura de textura,


use_v_normals boolean, boolean use_v_colors) {
System.out.println ( "\ t \ t \ t \ tcoordIndex [");
for (int i = 0; i <object.getNumberOfFaces (); + + i) {
Face = cara object.getFace (i);
if (face.useVertexNormals! = use_v_normals
| | Face.useVertexColors! = Use_v_colors)
continuar;
if (textura == null) (
/ / En busca de caras untextured
if (face.getTexture ()! = null)
/ / Esto es una textura
continuar;
}
else if (! texture.match (face.getTexture ()))
/ / Buscar partido
continuar;
System.out.print ( "\ t \ t \ t \ t \ t");
for (int j = 0; j <face.getNumberOfPoints (); - j)
System.out.print (face.getPoint (j) + "");
System.out.println ( "-1");
if (face.isDoublesided ()) (
System.out.print ( "\ t \ t \ t \ t \ t");
for (int j = face.getNumberOfPoints () -1;
j> = 0; - j)
29

VRML 2.0 con Java CAPÍTULO 13

System.out.print (face.getPoint (j) + "");


System.out.println ( "-1");
}
}
System.out.println ( "\ t \ t \ t \ t]");
}

Sólo la producción para generar esos rostros que coincidan con la configuración
de boolean use_v_normals y use_v_colors y también coincide con la textura que
se esté trabajando (una vez más utilizar nuestra Texture.match () para este
método). Vamos por los puntos en la cara, poner a cada uno, y virar un -1 en el
extremo (ya que ésa es la forma en VRML marca el final de una cara). Si una
cara es de doble cara (es decir, se ha establecido tanto en el archivo original
NFF), ponemos a los índices de nuevo en el orden inverso.

Dumping cara colores es similar, ya que tenemos que volcar los índices de
color en el mismo orden que los polígonos están en el archivo:

static void dump_face_colors (Obj objeto, de textura de textura, boolean


use_v_normals) {
if (dumped_face_colors)
System.out.println ( "\ t \ t \ t \ TColor USO FC");
else {
System.out.println ( "\ t \ t \ t \ TColor DEF FC (color");
System.out.println ( "\ t \ t \ t \ t \ TColor [");
for (int i = 0; i <object.getNumberOfFaces (); + + i) {
F = object.getFace cara (i);
if (f.getColor ()! = null)
System.out.println ( "\ t \ t \ t \ t \ t \ t" + f.getColor ());
algo más
System.out.println ( "\ t \ t \ t \ t \ t \ t0 0 0");
}
System.out.println ( "\ t \ t \ t \ t \ t]");
System.out.println ( "\ t \ t \ t \ t)");
dumped_face_colors = true;
}
System.out.println ( "\ t \ t \ t \ tcolorIndex [");
for (int i = 0; i <object.getNumberOfFaces (); + + i) (
F = object.getFace cara (i);
if (f.useVertexNormals! = use_v_normals
| | F.useVertexColors! = False) / / desfase
continuar;
if (textura == null) (/ / en busca de caras untextured
if (f.getTexture ()! = null) / / pero de una textura
30

VRML 2.0 con Java CAPÍTULO 13

continuar;
}
else if (! texture.match (f.getTexture ()))
/ / Para ver un partido
continuar;
System.out.println ( "\ t \ t \ t \ t \ t" + i);
if (f.isDoublesided ())
System.out.println ( "\ t \ t \ t \ t \ t" + i);
}
System.out.println ( "\ t \ t \ t \ t]");
}

Una vez más, cómo Aviso DEF y USE vamos a guardar un espacio considerable
en el archivo de salida.

El dump_appearance () Método

El último método veremos es la que vuelca el nodo a la apariencia de archivos


VRML:

static void dump_appearance (textura de textura) {


System.out.println ( "\ t \ t \ (tappearance Apariencia");
if (textura == null) {
System.out.println ( "\ t \ t \ t \ tmaterial Material ()");
System.out.println ( "\ t \ t \ t \ t)");
retorno;
}
System.out.println ( "\ t \ t \ t \ ttexture ImageTexture (url \" "
Texture.getFilename + ()
+ ". Jpg \") ");
System.out.println {
"\ t \ t \ t \ ttextureTransform TextureTransform (");
if (texture.getScale ()! = 1f)
System.out.println ( "\ t \ t \ t \ t \ tscale"
Texture.getScale + () + "" + texture.getScale ());
if (texture.getTransS ()! = 0 | | texture.getTransT ()! = 0)
System.out.println ( "\ t \ t \ t \ t \ t \ ttranslation"
Texture.getTransS + () + "" + texture.getTransT ());
if (texture.getRotation ()! = 0)
System.out.println ( "\ t \ t \ t \ t \ t \ trotation"
Texture.getRotation + ());
if (texture.getMirror ())
System.out.println ( "\ t \ t \ t \ t \ t # Nota: espejo ignorado");
31

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ t \ t)");


System.out.println ( "\ t \ t \ t)");
}

Como puede ver, es muy sencillo. Generamos un ImageTexture nodo cuyo


campo url referencias la textura de que se trate, con una extensión. Jpg
extensión añadió. Tendremos que convertir los archivos que el Targa Sense8 de
referencia en los archivos JPEG, lo que se puede hacer con una utilidad de
imagen como la Alquimia.

También pusimos un TextureTransform y emiten sólo a los ámbitos que son


diferentes de sus valores por defecto (una vez más, para ahorrar espacio).

Desde nff2vrml es una aplicación Java, en contraposición a un applet, lo


ejecuta como sigue:

java nff2vrml somefile.nff> somefile.wrl

Eso es todo lo que a él. Figura 13.1 muestra el resultado de la conversión de un


archivo a NFF VRML.

Figura 13.1 Un engranaje traducido del NFF a VRML

Conclusiones

El NFF a VRML convertidor que hemos escrito aquí no es perfecto, y que puede
producir algunos muy grandes archivos de salida. Sin embargo, ilustra algunos
principios importantes en la conversión de formatos de archivo legado VR en
VRML.
Quake MDL

Una pequeña empresa con sede en Texas, llamado id Software ha ganado un


buen nombre para sí mismo a través de los años con una sucesión de estado
de la técnica 3-D juegos de ordenador. Su más reciente producto de Quake, un
calabozo de rastreo-juego con gráficos muy sofisticados.

Además de un iluminado, mapeado de texturas medio ambiente, también


cuenta con animados Quake poligonal caracteres que se encuentran en 3-D. En
esta sección, vamos a examinar los formatos de archivo que utiliza Quake. En
particular, vamos a mirar en la línea de demarcación militar (modelo) el
formato que se utiliza para representar caracteres en el juego.
32

VRML 2.0 con Java CAPÍTULO 13

Antes de ir más lejos, es importante que te das cuenta de que los personajes
de la distribución oficial de Quake no son de dominio público, que son
propiedad de id Software, que posee los derechos de autor sobre los
personajes. No está permitido utilizarlos en sus propias aplicaciones a menos
que usted consigue el permiso expreso y por escrito de id Software.
Formato de archivo

Todos los datos para Quake se almacena en un único gran archivo, que se
compone de un gran número de subarchivos. Subfile cada uno tiene un nombre
de fichero y la longitud, así como una compensación en el conjunto de archivo.
En efecto, el archivo que contiene su propio sistema de ficheros.

El subarchivos almacenar una gran cantidad de diferentes tipos de información,


y utilizan una variedad de formatos de archivo. Los sonidos se almacenan como
archivos WAV, BSP árbol de datos se almacena en un formato especial que
incluye las regiones de visibilidad de información, y así sucesivamente. El
formato subfile particular estamos interesados en el MDL es.

El MDL tiene un archivo de cabecera que comienza con la secuencia de cuatro


bytes IDPO (por "id polígono" formato). El encabezado define cosas como el
número de versión, la escala, el origen, el desplazamiento de la posición de los
ojos de la criatura, y el radio de la figura. También almacenados en la cabecera
es la información acerca de las dimensiones de los mapas de texturas que se
van a aplicar a la cifra, el número de vértices, el número de triángulos, y el
número de fotogramas de animación.
Pieles

Después de la cabecera son los mapas de textura, lo que en terminología de


Quake especificaciones se denominan "pieles." Ellos se guardan en un formato
binario en bruto, con un fondo superior a pedido. Es decir, la primera scanline
del valor de los datos de la última línea en la parte inferior de la imagen, y la
última scanline en el expediente es la primera fila de datos en la pantalla. Las
pieles son un byte de profundidad, para que cada píxel es un índice en un
mapa de color.

Una persona de piel por lo general consta de dos partes. La mitad izquierda de
la imagen es para la parte frontal de la figura, y la mitad derecha de la imagen
es para la parte de atrás. Puede haber otras imágenes más pequeñas en el
mapa de textura y, por lo general en pares (como el anverso y el reverso de la
pistola del jugador).
Coordenadas de textura

El siguiente punto en el archivo es un conjunto de coordenadas de textura.


Cada elemento de la matriz de s tiene un valor y en valor, que se compensa el
33

VRML 2.0 con Java CAPÍTULO 13

entero en el mapa de textura.

Cada elemento tiene también una bandera que indica si este es coordinar la
textura de la "costura" entre la parte delantera y trasera texturas, cuando este
par de coordinar la textura se utiliza en un triángulo que hay en la parte
posterior de la figura, un offset horizontal de la mitad de los ancho de la
textura del mapa se añade al valor de s para que la imagen de la parte
posterior de la figura (en la mitad derecha de la imagen de textura) se hace
referencia en lugar de la parte delantera. Cuando esta misma textura coordinar
par se utiliza en un triángulo en la parte frontal de la figura, no compensado, se
añade, por lo que la mitad izquierda de la imagen de textura se hace
referencia.

Triángulo de Datos

La textura es coordinar los datos de inmediato seguido por los datos de los
triángulos. Este es un simple conjunto de triángulos, cada uno de los cuales
tiene tres valores de 32 bits que son los índices en la matriz para cada uno de
los vértices del triángulo de tres puntos. Esto es muy similar a la cara de datos
en formato NFF examinamos anteriormente, excepto que, en un archivo MDL,
las caras son siempre triángulos. El vértice real los datos se almacenan en cada
uno de animación, tal como se describe a continuación.

Cada cara tiene una bandera que indica si es parte de la parte delantera de la
modelo o de una parte de la espalda. Esta bandera se utiliza en conjunción con
la bandera onseam que está almacenado con la textura de los vértices, como
se ha descrito anteriormente.

Animación Marcos

Por último, hay un conjunto de cuadros de animación. La animación de la


tienda de marcos de los vértices de coordenadas, lo que significa que la cifra
puede cambiar de forma, así como mover sus extremidades, en los marcos
consecutivos. Dado que todos los polígonos son triángulos, no hay
preocupaciones acerca de la no-convexo o no planas que surge cuando se
enfrenta a los vértices moverse.

Los marcos son almacenados en una simple matriz. Cada cuadro tiene un
nombre, un par de puntos que dan limitan el tamaño de la criatura de este
marco de animación, y un conjunto de vértices. Fotogramas de vez en cuando
vienen en grupos, con los plazos de información asociados con el grupo.
34

VRML 2.0 con Java CAPÍTULO 13

En el marco, cada vértice es simplemente la x, y y z coordenadas (un byte cada


uno), además de un índice de 1-byte en una serie de precalculated vértice
normales que se utilizan para el buen sombreado (norma similar a la
información en el NFF formato). La x, y y z coordenadas deben ser ampliadas
por el valor de escala en la línea de demarcación militar encabezado del
archivo, y luego pasó por el valor de origen de la cabecera para obtener el
modelo de espacio de coordenadas el vértice.
Factores que complica

Todo esto parece bastante claro. Sin duda, este convertidor será más fácil de
escribir que la que hemos construido para el NFF formato? Bueno, no
necesariamente. Hay varios factores que hacen que el formato de Quake
complicado trabajar con él.
Pedidos byte

Todos los archivos están en Quake binario, y que utilizan los nativos de bytes
pedidos en la plataforma PC. Esta orden es la inversa de la utilizada por Java, lo
que significa que tendrá que girar la orden de cada byte entero y flotador que
leer el archivo.
Traslado de Vértices

Desde el vértice coordenadas para cambiar la cifra de marco a marco, tenemos


que ser capaces de simular este comportamiento cambia de forma en VRML.
Mapas de textura interna

Los mapas de texturas no se almacenan en archivos externos en un formato


conocido, como es el caso de NFF. En lugar de ello, sino que están
almacenados en el propio archivo, lo que significa que tendrá que extraer y
convertir a un formato estándar que un navegador de VRML puede leer.
Documentación parcial

El último factor que complica la situación es que la documentación es


fragmentaria e incompleta. La mayor parte de lo que se refiere a partir de la
línea "Quake Espec 3,2", elaborado por un talentoso grupo de entusiastas de
Quake encabezada por Olivier Montanuy, con un poco de ayuda de id Software.
La versión 3.2 de la especificación de la versión 6 se describe el formato de
archivo, una versión 3 se utilizó el formato en un anteproyecto "qtest" (prueba
de Quake) en libertad, pero la versión 6 parece ser la "oficial" de uno. Usted
puede encontrar el Quake especificaciones oficiales de Quake en el sitio de
soporte de edición (http://www.games.org/dEngine/quake/spec/qspec-
current.html).
MDL extraer archivos del paquete de archivos

Como se indicó anteriormente, todos los datos para un mundo Quake se


35

VRML 2.0 con Java CAPÍTULO 13

almacena en un único gran archivo. Este archivo, llamado "paquete" de archivo


(con extensión. PAK) tiene un encabezado que comienza con un 4-byte de la
firma PACK. La cabecera también tiene el desplazamiento en el archivo donde
se encuentra el directorio, y el tamaño del directorio. El directorio se compone
de una serie de entradas que contiene un nombre de archivo de 50 bytes con
estilo Unix / separadores, un desplazamiento del inicio de la subfile referencia,
y el tamaño de ese subfile.

Nuestro enfoque es escribir una pequeña, sencilla aplicación Java que puede
leer y extraer archivos de un archivo de Quake. Lo utilizaremos para extraer los
archivos de la línea de demarcación militar, y la utilización de una aplicación
Java para convertir cada archivo MDL en VRML.

El programa de extracción de archivos de Quake, que vamos a estar llamando


Qar, es muy simple. Su sintaxis de línea de comandos es una reminiscencia de
la ar o Unix tar los servicios públicos o la utilidad de DOS pkunzip. La línea de
comandos tiene un carácter único parámetro que indica que operación a
realizar en el archivo, sólo la vamos a apoyar las operaciones se t para obtener
un cuadro de el contenido del archivo, y x para extraer subarchivos.

Por defecto, se QAR pak0.pak leer el archivo en el directorio actual. Si lo desea,


puede especificar un archivo diferente o el nombre de ruta a raíz de la carta de
comando. Por ejemplo:

java no pak15.pak QAR

produciría una lista de todos los subarchivos que figuran en el archivo


pak15.pak PACK.

Si usted le da sólo el comando carta (y, opcionalmente, el nombre de archivo


PACK), QAR tabulará o extraer todos los archivos en el archivo. Si desea incluir
o extraer los archivos a partir de una cadena, puede especificar un parámetro
adicional después de que el nombre de archivo PACK. Dado que el archivo
contiene PACK estilo Unix nombres de ruta, con directorios, resulta ser muy útil.
Por ejemplo, desde la línea de demarcación militar todos los archivos se
almacenan en un directorio progs, el siguiente comando:

java QAR pak0.pak x progs

que va a extraer la totalidad de ellas, mientras que el siguiente comando:

java QAR pak0.pak x progs / demon.mdl

que va a extraer sólo el archivo demon.mdl. Tenga en cuenta que primero debe
36

VRML 2.0 con Java CAPÍTULO 13

crear manualmente un directorio progs para el archivo de entrar en; QAR no


construir la estructura de directorios para usted.

La lista a continuación muestra el código fuente Java para la QAR utilidad:

/ / Quake archivo de clase

/ / Escrito por Bernie Roehl, octubre de 1996

/ / Sobre la base de las especificaciones 3.2 de Quake documento

paquete terremoto;

importación java.io. *;

clase pública QAR {


public static void main (String [] args) throws IOException {
if (args.length <1) {
System.err.println{
"Uso: QAR cmd [nombre de archivo [nombre [... ]]]");
System.exit (1);
}
Cadena cmd = args [0];
Cadena de nombre de archivo = (args.length> 1)?
args [1]: "pak0.pak";
RandomAccessFile quakefile =
nuevo RandomAccessFile (nombre_archivo, "r");
if (quakefile.readInt ()! = 0x5041434B) {
System.err.println ( "falta la firma");
System.exit (2);
}
int diroffset = swap32 (quakefile.readInt ());
int dirsize = swap32 (quakefile.readInt ());
int n_direntries = dirsize / 64;
quakefile.seek (diroffset);
Cadena de prefijo = "";
if (args.length> 2)
prefix = args [2];
for (int i = 0; i <n_direntries; + + i) {
byte [] fname = new byte [0x38];
quakefile.read (fname);
Subfilename cadena = new String (fname, 0);
int zerobyte = subfilename.indexOf (0);
37

VRML 2.0 con Java CAPÍTULO 13

if (zerobyte> = 0)
subfilename =
subfilename.substring (0, zerobyte);
int offset = swap32 (quakefile.readInt ());
int size = swap32 (quakefile.readInt ());
if (cmd.charAt (0) == 't') {
if (subfilename.startsWith (prefijo))
System.out.println (tamaño + ""
+ Subfilename);
}
else if (subfilename.startsWith (prefijo)) {
System.out.println ( "Extraer"
+ Subfilename);
FileOutputStream out =
nuevo FileOutputStream (subfilename);
largo savedpos =
quakefile.getFilePointer ();
quakefile.seek (offset);
byte [] buffer = new byte [1024];
do {
int n = quakefile.read (buffer);
out.write (buffer, 0, n);
-= tamaño n;
) While (tamaño> 0);
quakefile.seek (savedpos);
}
}
}

static int swap32 (int valor) {


volver
((valor>> 24) y 0x000000FF) +
((valor>> 8) y 0x0000FF00) +
((valor <<8) y 0x00FF0000) +
((valor <<24) y 0xFF000000);
}
}

Hay varias cosas a la nota en la lista anterior. La más importante es la


inversión de bytes pedidos que hemos hablado antes; la swap32 () método
tiene un entero de 32 bits y devuelve un byte-versión de lo invertido por el
desplazamiento y ocultación.

El método main () abre el archivo, usando el nombre de archivo especificado si


38

VRML 2.0 con Java CAPÍTULO 13

hay una, de otro modo utilizar la opción predeterminada. A continuación, lee y


verifica la firma. No molestan a byte-invertir aquí, sino que acabamos de
comparar contra el valor hexadecimal en bruto que se deriva de la lectura en el
byte de Java utilizando el pedido.

El siguiente paso es leer el offset y el tamaño del directorio, calcular el número


de entradas de directorio (cada entrada es de 64 bytes de longitud), y buscar
en el directorio en sí. Además, el prefijo que usaremos para equiparar las
subfile nombre es recogido, que por defecto a una cadena vacía, lo que
coincide con todos los subarchivos (ya que todos comienzan con nada).

Ahora pasan por el directorio de entradas. Para cada uno, se lee el nombre de
archivo de 50 bytes de información y convertirla en una cadena. Desde Quake
utiliza un C-estilo, nulo terminado cadena de caracteres para almacenar el
nombre del archivo, nos encontramos con el offset del primer byte cero y el
uso de la sub-hasta ese momento como nombre de archivo.

A continuación, leemos el offset y el tamaño de la subfile. Observe que usamos


el swap32 () método para revertir el byte pedido.

Si el comando está en el carácter, simplemente imprimir el tamaño y la subfile


nombre para los archivos de inicio con el prefijo. El String.startsWith () método
es muy útil para esto.

Si el comando personaje es otra cosa, es que se supone x. Si este archivo


coincide con el prefijo, salvo que nuestra actual ubicación en el directorio y
buscar el comienzo de la subfile. Creamos un nuevo archivo con el mismo
nombre que el subfile y copiar en él el subfile. Una vez terminado, queremos
volver a donde estábamos en el directorio y continuar procesando el resto de
subarchivos.

La aplicación de la línea de demarcación militar Parser

Ahora que tenemos la línea de demarcación militar de archivo en la mano,


podemos empezar a analizar la misma. Nuestro planteamiento será muy
similar a la que hemos utilizado para NFF archivos, en el que tendremos una
clase para cada uno de los principales tipos de datos se encuentran en la línea
de demarcación militar archivo: Piel, TextureVertex, Trivertex, FrameGroup, y
Marco. Vamos a tener también una llamada MDLFile, lo que representa todo el
archivo de MDL de la misma manera que una escena representada toda una
NFF archivo.

Vamos a utilizar también un par de clases de utilidad: Vec3 maneja vectores en


39

VRML 2.0 con Java CAPÍTULO 13

tres dimensiones, de la misma manera que hemos usado Vec3f por el


equivalente ASCII vectores. La clase ByteFlipInputStream dice de 32 bits
enteros y números de punto flotante con volteo automático-byte.

Vamos a utilizar una clase llamada de BMP para la escritura de archivos de


Windows BMP para guardar nuestros mapas de texturas. Estamos utilizando el
formato BMP, por varias razones: Es sencillo, es ampliamente apoyada por los
programas de pintura para el entorno Windows, y su parte inferior hasta la
cima scanline pedido que los partidos de los mapas de textura en los archivos
de la línea de demarcación militar.

La clase Vec3

Vec3 la clase es similar a la Vec3f clase, sino que utiliza los readFlippedFloat ()
de la ByteFlipInputStream método para obtener sus valores:

/ / Clase para el manejo de Quake 3-elemento vectores

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública Vec3 {


protegidas flotar x, y, z;

flotador público getX () (return x;)


flotador público Gety () (return y;)
flotador público Getz () (return z;)

public String toString () (


volver x + "" + y + "" + z;
}

Vec3 público (ByteFlipInputStream entrada) throws IOException {


x = input.readFlippedFloat ();
y = input.readFlippedFloat ();
z = input.readFlippedFloat ();
}
}

Usted puede preguntarse por qué no acaba de volver a utilizar el Vec3f clase, y
40

VRML 2.0 con Java CAPÍTULO 13

añadir los métodos para hacer frente a la Quake-byte vuelta el pedido. Así,
sería la clase Vec3f menos genérico, ya que se sabe acerca de byte WAV y la
ByteFlipInputStream. Podríamos, sin embargo, han optado por hacer una
subclase Vec3 de Vec3f.

La clase ByteFlipInputStream

Orientado a la lectura de bytes de datos (tales como mapas de texturas) es


straighforward, ya pedido octetos no es un problema. Sin embargo, la línea de
demarcación militar utiliza el formato de archivo de 32 bits enteros y flotantes,
que tienen que ser volteado-byte como son leer. Nuestro ByteFlipInputStream
maneja esta clase. Es una subclase de DataInputStream, con los métodos de
lectura-byte volteado enteros y carrozas:

/ / La clase para hacer frente a pedidos de bytes

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública se extiende ByteFlipInputStream (DataInputStream

protegidas byte [] buffer;

protegidas en DataInputStream;

público ByteFlipInputStream (DataInputStream entrada) {

super (entrada);
= entrada en;
= new byte buffer [4];
}

público final int readFlippedInt () throws IOException {


buffer [3] = in.readByte ();
buffer [2] = in.readByte ();
buffer [1] = in.readByte ();
buffer [0] = in.readByte ();
DataInputStream d = new DataInputStream {
nuevo ByteArrayInputStream (buffer));
volver d.readInt ();
41

VRML 2.0 con Java CAPÍTULO 13

público final readFlippedFloat flotador () throws IOException {


buffer [3] = in.readByte ();
buffer [2] = in.readByte ();
buffer [1] = in.readByte ();
buffer [0] = in.readByte ();
DataInputStream d = new DataInputStream {
nuevo ByteArrayInputStream (buffer));
volver d.readFloat ();
}
}

Usted puede preguntarse por qué no acaba de hacer el desplazamiento de bits


y de enmascaramiento que se utilizó en la swap32 () método de Qar. Este
enfoque funciona bien para los enteros, pero es un poco desordenado para
números de punto flotante. La técnica que usamos aquí es la construcción de
un buffer de 4 bytes, en leer cuatro bytes en orden inverso, a continuación,
cree un DataInputStream de él y sólo utilizar el readInt () y readFloat ()
métodos.
El BMP Clase

El BMP clase escribe un archivo de Windows BMP. Se necesita un nombre de


archivo de salida, una matriz de bytes, la anchura y la altura de la imagen, y
una referencia a la paleta a utilizar, y escribe la paleta y la imagen a ese
archivo:

/ / Archivo BMP escritor

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública BMP {


public void estática de volcado (String filename, byte [] imagen,
int width, int height, byte [] paleta)
lanza IOException {
DataOutputStream out = nuevo DataOutputStream {
nuevo FileOutputStream (filename));
/ / Escribir el archivo de cabecera
out.writeByte ( 'B');
out.writeByte ( 'M');
42

VRML 2.0 con Java CAPÍTULO 13

/ / Tamaño total es sizeof archivo bmp sizeof + cabecera cabecera


/ / + + Sizeof paleta bits
out.writeInt (swap32 (14 + 40 + 1024 + ancho * alto));
out.writeInt (0);
out.writeInt (swap32 (14 +40 +1024)); / / compensar a los bits
/ / Escribir cabecera BMP
out.writeInt (swap32 (40)); / / tamaño de mapa de bits de cabecera
out.writeInt (swap32 (ancho)); / / anchura
out.writeInt (swap32 (altura)); / / altura
out.writeInt (0x01000800);
/ / Son próximos a cero para los impagos
out.writeInt (0); / / Tipo de compresión
out.writeInt (0); / / tamaño del mapa de bits
out.writeInt (0); / / resolución horizontal
out.writeInt (0); / / resolución vertical
out.writeInt (0); / / número de colores
out.writeInt (0); / / # importante de los colores
out.write (paleta); / / escribir paleta
out.write (foto); / / escribir bits
}

static int swap32 (int valor) {


volver
((valor>> 24) y 0x000000FF) +
((valor>> 8) y 0x0000FF00) +
((valor <<8) y 0x00FF0000) +
((valor <<24) y 0xFF000000);
}
}

Note que estamos usando el método swap32 de nuevo, lo que sugiere que
probablemente deba estar en una clase separada.

El encabezado del archivo de salida se inicia con las letras BM (de mapa de
bits). Esto es seguido por el tamaño total del archivo, una palabra cero, el
desplazamiento de la imagen real, y el mapa de bits de cabecera. El mapa de
bits de cabecera tiene el tamaño de la cabecera, la anchura y la altura de la
imagen, y la información adicional que puede ser puesta a cero ya que los
valores por defecto serán asumidas por cualquier utilidad que lee ficheros BMP.

Esto es seguido por la paleta y finalmente por los propios píxeles. Como ya
comentamos anteriormente, los píxeles se organizan en una parte inferior a la
superior dirección, lo que significa que no necesitamos hacer nada con ellos
más allá del escribir a cabo.
43

VRML 2.0 con Java CAPÍTULO 13

La clase MDLFile

La clase MDLFile almacena toda la información se encuentra en la cabecera de


un archivo de MDL, además de conjuntos de pieles, la textura vértices,
triángulos, y el marco grupos. Métodos de acceso están disponibles para todos
los campos. El constructor MDLFile lee el archivo de un MDL DataInputStream:

/ / Una clase que representa un archivo de Quake MDL

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública MDLFile {


int versión protegidas; / / número de versión
protegidas Vec3 escala; / / factores de escala
Vec3 origen protegidas; / / origen del modelo
flotador radio protegidas; / / radio de la envolvente esfera
protegidas Vec3 compensaciones; / / posición de los ojos
int numskins protegidas; / / número de pieles
int skinwidth protegidas; / / debe ser múltiplo de 8
int skinheight protegidas; / / debe ser múltiplo de 8
int numverts protegidas, numtris, numframes;
int synctype protegidas; / / 0 = Synchron, 1 = aleatorio
int banderas protegidas;
flotador tamaño protegidas; / / tamaño medio de los triángulos

Piel protegida [] pieles; / / matriz de pieles


TextureVertex protegidas [] textureVertices;
Triángulo protegidas [] triángulos; / / conjunto de triángulos
FrameGroup protegidas [] framegroups; / / marco de los grupos

público int getVersion () (return versión;)


público Vec3 getScale () (return escala;)
público Vec3 getOrigin () (return origen;)
flotador público getRadius () (return radio;)
público Vec3 getOffsets () (return compensaciones;)
público int getNumberOfSkins () (return numskins;)
público int getSkinWidth () (return skinwidth;)
público int getSkinHeight () (return skinheight;)
44

VRML 2.0 con Java CAPÍTULO 13

público int getNumberOfVertices () (return numverts;)


público int getNumberOfTriangles () (return numtris;)
público int getNumberOfFrameGroups () (
numframes retorno;
}
público int getSyncType () (return synctype;)
público int getFlags () (return banderas;)
flotador público getSize () (return tamaño;)
Cuidado getSkin público (int n) (return pieles [n];)
público TextureVertex getTextureVertex (int n) {
volver textureVertices [n];
}
Triángulo getTriangle público (int n) (return triángulos [n];)
público FrameGroup getFrameGroup (int n) {
volver framegroups [n];
}

MDLFile público (en DataInputStream)


lanza MDLFormatException, IOException (
ByteFlipInputStream entrada =
ByteFlipInputStream nuevo (en);
/ / Leer cabecera
if (input.readFlippedInt ()! = 0x4F504449)
lanzar nuevos MDLFormatException {
"mala la firma");
if ((version = input.readFlippedInt ())! = 6)
lanzar nuevos MDLFormatException {
"sólo apoya la versión 6, el archivo es la versión"
+ Versión);
escala = new Vec3 (entrada);
origen = new Vec3 (entrada);
radio = input.readFlippedFloat ();
compensaciones = new Vec3 (entrada);
numskins = input.readFlippedInt ();
skinwidth = input.readFlippedInt ();
skinheight = input.readFlippedInt ();
numverts = input.readFlippedInt ();
numtris = input.readFlippedInt ();
numframes = input.readFlippedInt ();
synctype = input.readFlippedInt ();
flags = input.readFlippedInt ();
size = input.readFlippedFloat ();
/ / Leer pieles
pieles = nueva piel [numskins];
45

VRML 2.0 con Java CAPÍTULO 13

for (int i = 0; i <numskins; + + i)


pieles [i] = new Skin {
de entrada, skinwidth, skinheight);
textureVertices = new TextureVertex [numverts];
for (int i = 0; i <numverts; + + i)
textureVertices [i] = new TextureVertex (entrada);
triángulos = new Triángulo [numtris];
for (int i = 0; i <numtris; + + i)
triángulos [i] = new Triángulo (entrada);
framegroups = new FrameGroup [numframes];
for (int i = 0; i <numframes; + + i)
framegroups [i] =
nuevo FrameGroup (entrada, numverts);
}
}

Una vez más, tenga en cuenta el control que ha hecho en contra de la firma.
Asimismo, para comprobar los archivos con un número de versión diferente, ya
que sólo admiten la versión 6. En particular, la versión 3 archivos no son
compatibles.

La escala, el origen, y las compensaciones son los tres elementos vectores de


punto flotante, que se leen mediante el Vec3 clase. La radio y el tamaño son
números de punto flotante, utilizando la lectura ByteFlipInputStream.readFloat
() método. Los tamaños de matriz (numskins, numverts, numtris y numframes),
la textura mapa dimensiones (skinwidth y skinheight), y las banderas y el valor
de sincronización (que no usamos) son enteros de 32 bits que se leen mediante
el ByteFlipInputStream. readInt () método.

Las pieles, la textura vértices, triángulos, y el marco son leídos por todos los
constructores de sus clases correspondientes.

La piel de la clase

Una piel en un archivo MDL puede consistir en una serie de imágenes, dispuestas en una
secuencia temporal, que permiten el uso de mapas de texturas animadas. La piel de clase
almacena el número de tales imágenes, una variedad de las imágenes, una serie de códigos
para el momento en las imágenes, y las dimensiones de la piel. Cada imagen es un arreglo
de alto * ancho de bytes, por lo que el píxeles [] [] matriz es un conjunto de imágenes (que
no es bidimensional matriz de bytes).

Existen métodos de acceso para obtener la imagen de una persona y su tiempo, así como el
número de imágenes y las dimensiones de la piel. También hay un método para obtener un
píxel individual de una imagen concreta:
46

VRML 2.0 con Java CAPÍTULO 13

/ / La piel de un archivo de Quake MDL

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública Cuidado {


protegidas int altura, anchura;
int npictures protegidas;
protegidas flotar [] veces;
protegidas byte [] [] pixels;

público int getHeight () (return altura;)


público int getWidth () (return ancho;)
público int getNumberOfPictures () (return npictures;)
flotador público getTime (int n) (return veces [n];)
público byte [] getPicture (int n) (return píxeles [n];)

público getPixel byte (int cuadro, int horiz, int vert) {


volver píxeles [Foto] [vert * anchura horizontal +];
}

Cuidado público (ByteFlipInputStream entrada, int w, int h)


lanza IOException {
height = h;
width = w;
if (input.readFlippedInt () == 0) {
npictures = 1;
veces = new flotar [1];
ocasiones [0] = 0;
píxeles = new byte [1] [alto * ancho];
input.read (píxeles [0]);
}
else {
npictures = input.readFlippedInt ();
veces = new flotar [npictures];
for (int i = 0; i <npictures; + + i)
veces [i] = input.readFlippedFloat ();
píxeles = new byte [npictures] [alto * ancho];
for (int i = 0; i <npictures; + + i)
input.read (píxeles [i]);
}
}
}
47

VRML 2.0 con Java CAPÍTULO 13

El constructor tiene la altura y la anchura de los valores que se lea el archivo de cabecera de
la línea de demarcación militar, junto con un puntero a la ByteFlipInputStream. La primera
palabra en la piel es una bandera, si es cero, sólo hay una foto con ningún momento los
valores. Si la bandera es un valor distinto de cero, es seguida por un recuento del número de
imágenes y una amplia gama de valores de tiempo (uno para cada foto).
La clase TextureVertex

El TextureVertex clase es trivial, compuesto de algunos métodos de acceso directo y un


constructor:

/ / Cuidado vértice MDL para un archivo de Quake

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública TextureVertex {


boolean onseam protegidas;
protegidas int s, t;

público isOnseam boolean () (return onseam;)


int público gets () (return s;)
Gett público int () (return t;)

TextureVertex público (ByteFlipInputStream entrada)


lanza IOException {
onseam = (input.readFlippedInt () == 0)? falso: la verdad;
s = input.readFlippedInt ();
t = input.readFlippedInt ();
}
}

El único truco es la bandera onseam; que convertirlo de un entero de 32 bits en un valor


booleano Java.
El Triángulo de la clase

El triángulo de clase también es muy sencillo:

/ / Triángulo clase MDL para la tramitación de expedientes

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;
48

VRML 2.0 con Java CAPÍTULO 13

clase pública Triángulo {


boolean facesfront protegidas;
protegidas int [] puntos = new int [3], / / vértice en los índices de matriz

público isFrontFacing boolean () (return facesfront;)

public String toString () {


retorno puntos [0] + "" + puntos [1] + "" + puntos [2];
}
público int getPoint (int n) (return puntos [n];)

Triángulo público (ByteFlipInputStream entrada) throws IOException {


facesfront =
(input.readFlippedInt () == 0)? falso: la verdad;
puntos [0] = input.readFlippedInt ();
puntos [1] = input.readFlippedInt ();
puntos [2] = input.readFlippedInt ();
}
}

El getPoint () devuelve el método de índice de un vértice del triángulo. Al igual que con el
TextureVertex clase, que convertir un entero de 32 bits a un valor pabellón Java booleano.

La clase FrameGroup

Un cuadro en un grupo de archivos MDL es una colección de marcos


individuales. Cada elemento en el marco del MDL archivo grupo matriz tiene
una bandera que indica si se trata de un simple marco o un grupo de
fotogramas simples:

/ / Frame grupo de archivo de un MDL

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública FrameGroup {


int nsubframes protegidas;
protegidas Trivertex min, max;
protegidas flotar [] veces;
Marco protegidas [] subframes;
49

VRML 2.0 con Java CAPÍTULO 13

público int getNumberOfSubFrames () (return nsubframes;)


público Trivertex getMin () (return min;)
público Trivertex getMax () (return max;)
flotador público getTime (int n) (return veces [n];)
público Marco getSubFrame (int n) (return subframes [n];)
FrameGroup público (ByteFlipInputStream entrada, int nverts)
lanza IOException {
int tipo = input.readFlippedInt ();
if (tipo == 0) {
nsubframes = 1;
veces = new flotar [1];
ocasiones [0] = 0;
subframes = new Frame [1];
subframes [0] = new Frame (entrada, nverts);
subframes min = [0]. getMin ();
max = subframes [0]. getMax ();
}
else {
nsubframes = input.readInt ();
min = new Trivertex (entrada);
máx = new Trivertex (entrada);
veces = new flotar [nsubframes];
for (int i = 0; i <nsubframes; + + i)
veces [i] = input.readFlippedFloat ();
subframes = new Frame [nsubframes];
for (int i = 0; i <nsubframes; + + i)
subframes [i] =
nuevo marco (de entrada, nverts);
)
)
)

Si el tipo de bandera es igual a cero, un simple cuadro siguiente. No existe una


lista de subframes y ningún conjunto de timecodes. El constructor de la clase
Marco dice un cuadro, cuyos valores mínimo y máximo se utilizan como los de
la estructura del grupo.

Si el tipo de bandera es un valor distinto de cero, por otro lado, las cosas son
más complejas. Leemos el número de subframes que siguen, los valores
mínimo y máximo para que el conjunto de marcos, el conjunto de códigos de
tiempo, y los marcos propios.

Todo esto puede parecer una reminiscencia de la forma en que leemos las
pieles, y los principios son los mismos. Sin embargo, la versión 3.2 de la
50

VRML 2.0 con Java CAPÍTULO 13

especificación de Quake tiene algunos errores en este ámbito, lo que creó


cierta confusión en el desarrollo de mdl2vrml. Desde el Quake especificaciones
y no oficiales son el resultado de muchas horas de análisis por entusiastas
aficionados, algunos de vez en cuando errores menores no son sorprendentes.

FrameGroup la clase tiene los métodos para la recuperación de un individuo y


su marco de código de tiempo, el número de subframes, y las esquinas de la
caja para todo el grupo de subframes.

El Marco de la clase

Un cuadro contiene el nombre de la imagen, el min y max los valores de los


vértices en el marco, y el conjunto de vértices:

/ / Marco de un simple archivo de MDL

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública Marco {


protegidas Trivertex min, max;
Cadena nombre protegidas;
protegidas Trivertex vértices [];
público Trivertex getMin () (return min;)
público Trivertex getMax () (return max;)
public String getName () (return nombre;)
público Trivertex getVertex (int n) (return vértices [n];)

público Frame (ByteFlipInputStream entrada, int nverts)


lanza IOException {
byte [] buffer = new byte [16];
min = new Trivertex (entrada);
máx = new Trivertex (entrada);
input.read (buffer);
nombre = new String (buffer, 0);
int zerobyte = name.indexOf (0);
if (zerobyte> = 0)
name = name.substring (0, zerobyte);
vértices = new Trivertex [nverts];
for (int i = 0; i <nverts; + + i)
51

VRML 2.0 con Java CAPÍTULO 13

vértices [i] = new Trivertex (entrada);


}
}

Utilizamos la misma técnica para convertir el nombre de marco a medida que


utilizan para convertir la subfilename en Qar. El constructor de la clase
Trivertex se usa para leer la vértices.

La clase Trivertex

Esta es otra clase trivial, la cual utiliza el DataInputStream.ReadUnsignedByte


() para leer la x, y y z del índice y las coordenadas de los vértices normales:

/ / Un vértice de un triángulo Quake MDL archivo

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete terremoto;

importación java.io. *;

clase pública Trivertex {


protegidas int x, y, z;
int lightNormalIndex protegidas;

público int getX () (return x;)


Gety público int () (return y;)
público int Getz () (return z;)
público int getLightNormalIndex () {
lightNormalIndex retorno;
}

public String toString () {


volver x + "" + y + "" + z + "(normal" + lightNormalIndex
+ ")";
}

público Trivertex (DataInputStream entrada) throws IOException {


x = input.readUnsignedByte ();
y = input.readUnsignedByte ();
z = input.readUnsignedByte ();
lightNormalIndex = input.readUnsignedByte ();
}
52

VRML 2.0 con Java CAPÍTULO 13

LightNormalIndex el campo se utiliza para indexar en un pre-calculada variedad


de vectores normales. Esto ahorra mucho espacio en el archivo de la línea de
demarcación militar, por supuesto, y no afecta a la prestación de precisión en
cualquier forma notable, ya que los vectores normales utilizados para el
alumbrado sólo necesitan ser aproximados.

Generación de la salida de VRML

Ahora que tenemos todo el MDL archivo cargado en la memoria, podemos


generar algo de VRML salida.

Vamos a dar algunas opciones que el usuario. Ellos pueden o no quieren que la
secuencias de animación, así que vamos a proporcionar un pabellón para
incluirlos. Que puede o no quiere los mapas de textura, por lo que vamos a
darles una bandera-t para que dicha función.

Analizar las opciones y la Generación de la cabecera

La lista a continuación muestra la primera parte de mdl2vrml.java:

/ / Leer un archivo de Quake MDL y de exportación como de VRML

/ / Escrito por Bernie Roehl, noviembre de 1996

importación java.io. *;
importación java.util .*;
importación terremoto .*;

clase pública mdl2vrml {


public static void main (String args [])
lanza MDLFormatException, IOException {
boolean = false texturas, animación = false;
int argnum = 0;
para (argnum = 0; argnum <args.length; argnum + +) {
if (args [argnum]. startsWith ( "-t"))
texturas = true;
else if (args [argnum]. startsWith ( "-a"))
animación = true;
algo más
break;
53

VRML 2.0 con Java CAPÍTULO 13

}
Mdl_filename cadena = new String (args [argnum]);
DataInputStream entrada = new DataInputStream {
new FileInputStream (mdl_filename));
MDL MDLFile = new MDLFile (entrada);
System.out.println ( "# VRML V2.0 utf8 \ n");
System.out.println ( "# de creación por mdl2wrl \ n");
System.out.println ( "# archivo original"
Mdl_filename + + "'");
System.out.println ( "# Los datos del archivo original:");
System.out.println ( "Modelo de radio # ="
Mdl.getRadius + ());
System.out.println ( "# = Escala"
Mdl.getScale + ());
System.out.println ( "# origen ="
Mdl.getOrigin + ());
System.out.println ( "# = Compensaciones"
Mdl.getOffsets + ());
System.out.println ( "#"
Mdl.getNumberOfVertices + ()
+ "Vértices");
System.out.println ( "#"
Mdl.getNumberOfTriangles + ()
+ "Triángulos");
System.out.println ( "#"
Mdl.getNumberOfFrameGroups + ()
+ "Frames");
System.out.println ( "#" + mdl.getNumberOfSkins ()
+ "Pieles, cada" mdl.getSkinWidth + () + "por"
Mdl.getSkinHeight + ());
System.out.println ();
si (animación)
System.out.println {
"Grupo de los niños ([DEF TOQUE TouchSensor ()");
System.out.println ( "Forma (");

Después de analizar las opciones, que carga el archivo en la línea de


demarcación militar. Fíjate en cómo esto es fácil:

MDL MDLFile = new MDLFile (entrada);

A continuación, apagar el estándar VRML 2.0 cabecera, junto con algunos


comentarios que contienen información básica sobre el archivo (incluyendo el
número de vértices y el número de triángulos, que son útiles para saber de
54

VRML 2.0 con Java CAPÍTULO 13

alguien que está reuniendo una gran mundo que necesita para que se ejecute
en tiempo real velocidades). Si estamos poniendo a la animación (es decir, si la
animación del pabellón tiene establecido por la opción-a en la línea de
comandos) que también puso un TouchSensor nodo que nos permita iniciar y
detener la animación después. Por último, ponemos en el inicio de la Forma de
nodo que contendrá esta criatura.
El procesamiento de los mapas de textura y apariencia de la Generación del
Nodo

Nuestro siguiente paso es poner la Apariencia nodo. Si el usuario no ha


solicitado ninguna texturas (es decir, que no especificó la opción-t en la línea
de comandos), que acabamos de establecer una diffuseColor de blanco. Sin
embargo, si el usuario no especifica una textura, las cosas se ponen más
complicadas:

System.out.println ( "\ (tappearance Apariencia");

if (texturas) {
int dot_offset = mdl_filename.lastIndexOf ('.');
Basename mdl_filename.substring cadena = (0,
dot_offset);
byte [] = paleta load_palette ( "paleta");
for (int i = 0; i <mdl.getNumberOfSkins (); + + i) {
S = mdl.getSkin piel (i);
for (int j = 0;
j <s.getNumberOfPictures (); j + +) {
byte [] imagen = s.getPicture (j);
Filename = cadena
new String (basename + i
+ "_" + J + ". Bmp");
BMP.dump (nombre de archivo, imagen,
mdl.getSkinWidth (),
mdl.getSkinHeight (),
paleta);
if (i! = 0 | | j! = 0)
System.out.print ("#");
System.out.println {
"\ t \ ttexture ImageTexture {
url \ "" + filename + "\") ");
}
}
}
System.out.println ( "\ t \ (tmaterial Material");
System.out.println ( "\ t \ t \ tdiffuseColor 1 1 1");
55

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t)");


System.out.println ( "\ t)");

Empezamos por la generación de una base de textura nombre de archivo el


nombre del archivo de entrada la línea de demarcación militar. También se
carga la textura de la paleta de un archivo llamado paleta (que encontrará en
el CD-ROM). Este es el load_palette () método:

estática byte [] load_palette (String filename)


lanza IOException, FileNotFoundException{
DataInputStream en = new DataInputStream{
new FileInputStream (filename));
byte [] = new byte amigo [1024];
for (int i = 0; i <1024; i + = 4) {
pal [i +2] = in.readByte (); / / rojo
pal [i +1] = in.readByte (); / / verde
pal [i 0] = in.readByte (); / / azul
pal [i +3] = 0; / / reservado
}
regreso amigo;
}

A continuación, pasar por todas las pieles, y por cada piel que pasar por todas
las imágenes que en la piel. Para cada imagen, se genera un nombre de
archivo de la forma basenameN_M.bmp, donde N es el número de la piel y M es
el número de sub-imagen. Usamos el BMP clase que hemos debatido antes de
que se haga realidad la textura archivos.

A continuación se generan una textura ImageTexture () campo para cada una


de las texturas. Sin embargo, todos los comentarios, pero el primero de ellos.
Esto permite al usuario posteriormente editar el archivo y cambiar el mapa de
textura que se acostumbra, sólo por comentar la primera línea y sin comentar
uno de los subsiguientes. También podrían tomar las BMP y convertirlas en un
MPEG usando algún otro utilidad y cambiar el ImageTexture en un
MovieTexture.

Generación de la IndexedFaceSet

Una forma de nodo se compone de un aspecto y una geometría de campo


sobre el terreno. Ahora que hemos acabado con la apariencia, es el momento
de realmente crear la geometría:

System.out.println ( "\ tgeometry");


56

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ (tIndexedFaceSet");

F = mdl.getFrameGroup Marco (0). GetSubFrame (0);

System.out.print ( "\ t \ tcoord");


si (animación)
System.out.print ( "C DEF");
System.out.println ( "Coordinar (");
System.out.println ( "\ t \ t \ tpoint [");
for (int i = 0; i <mdl.getNumberOfVertices (); + + i)
emit_vertex (f.getVertex (i), MDL);
System.out.println ( "\ t \ t \ t]");
System.out.println ( "\ t \ t)");

System.out.print ( "\ t \ tnormal");


si (animación)
System.out.print ( "DEF N");
System.out.println ( "(normal");
System.out.println ( "\ t \ t \ tvector [");
for (int i = 0; i <mdl.getNumberOfVertices (); + + i)
emit_normal (f.getVertex (i), MDL);
System.out.println ( "\ t \ t \ t]");
System.out.println ( "\ t \ t)");

El primer paso es poner la geometría IndexedFaceSet (cabecera. Tenemos que


generar los vértices de coordenadas reales, de modo que seleccione el primer
cuadro de la primera imagen del grupo y poner un campo coord contiene un
nodo de coordenadas. Si vamos a animar las coordenadas, se añade un sello
DEF C delante de la Coordinación de nodo para que podamos VÍA a ella más
tarde. El emit_vertex () sea el método utilizado para realmente poner en los
vértices. A continuación, hacer exactamente lo mismo para el vértice normales,
utilizando emit_normal ().

Aquí están los emit_vertex () y emit_normal () métodos. Son bastante sencillo-,


tenga en cuenta que hemos omitido el bien de la brevedad de los 162 vectores
de tres elementos que forman la pre-calculada normales [] matriz:

static void emit_vertex (Trivertex vértice, MDLFile MDL) {


flotador vrmlScale = 0.0254f;
System.out.println ( "\ t \ t \ t \ t"
+ VrmlScale * (vertex.getX () * mdl.getScale (). GetX ()
Mdl.getOrigin + (). GetX ()) + ""
+ VrmlScale * (vertex.getZ () * mdl.getScale (). Getz ()
Mdl.getOrigin + (). Getz ()) + ""
57

VRML 2.0 con Java CAPÍTULO 13

+ VrmlScale * (vertex.getY () * mdl.getScale (). Gety ()


Mdl.getOrigin + (). Gety ())
};
}

static double [] [] = (<very normales larga lista de numbers>);

static void emit_normal (Trivertex vértice, MDLFile MDL) {


doble normal [] =
normales [vertex.getLightNormalIndex ()];
System.out.println ( "\ t \ t \ t \ t"
+ Normal [0] + "" + normal [2] + "" + normales [1]);
}

Aviso de cómo traducir la escala y las coordenadas en el vértice emit_vertex ()


con los valores del archivo de cabecera de la línea de demarcación militar.

Generación de las caras es fácil:

System.out.println ( "\ t \ tcoordIndex [");


for (int i = 0; i <mdl.getNumberOfTriangles (); + + i) {
Triángulo t = mdl.getTriangle (i);
if (t.getPoint (0)! t.getPoint = (1) & &
t.getPoint (1)! = t.getPoint (2) & &
t.getPoint (2)! = t.getPoint (0))
System.out.println ( "\ t \ t \ t" + t.getPoint (0) + ""
+ T.getPoint (1) + "" + t.getPoint (2) + "-1");
}
System.out.println ( "\ t \ t]");

Nos enfrenta a degenerar salto (en los que se enfrenta a dos de los vértices
son idénticos). Para cada uno de no saltarse la cara, que emiten los mismos
índices, seguido de un -1 para indicar el final de una cara en VRML.

Generación de coordenadas de textura

Si somos la generación de coordenadas de textura, ponemos un texCoord


TextureCoordinate (cabecera. A continuación, las coordenadas de textura emite
dos veces por una vez con el ordinario de valores, y una vez con los valores de
s compensado por skinwidth / 2, tal como se ha descrito anteriormente:

if (texturas) {
System.out.println ( "\ t \ ttexCoord TextureCoordinate (");
58

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ t \ t \ tpoint [");


for (int i = 0; i <mdl.getNumberOfVertices (); + + i) (
TextureVertex v = mdl.getTextureVertex (i);
System.out.print ( "\ t \ t \ t \ t"
+ (V.getS () / (float) mdl.getSkinWidth ())
+ "" + (V.getT () / (float) mdl.getSkinHeight ()));
if (v.isOnseam ())
System.out.print ( "# onseam");
System.out.println ();
}
for (int i = 0; i <mdl.getNumberOfVertices (); + + i) {
TextureVertex v = mdl.getTextureVertex (i);
System.out.print ( "\ t \ t \ t \ t"
+ ((V.getS () + mdl.getSkinWidth () / 2)
/ (float) mdl.getSkinWidth ())
+ "" + (V.getT () / (float) mdl.getSkinHeight ()));
if (v.isOnseam ())
System.out.print ( "# onseam");
System.out.println ();
}
System.out.println ( "\ t \ t \ t]");
System.out.println ( "\ t \ t)");

Observe que dividimos las coordenadas de textura por la anchura y la altura de


la textura de ruta, ya que utiliza VRML coordenadas de textura en el rango de
0.0 a 1.0, en vez de cruda textura mapa compensaciones.

La generación de los índices de coordenadas de textura

El siguiente paso es generar los índices en la matriz de coordenadas de textura


que hemos producido:

System.out.println ( "\ t \ ttexCoordIndex [");


for (int i = 0; i <mdl.getNumberOfTriangles (); + + i) {
Triángulo t = mdl.getTriangle (i);
if (t.getPoint (0) == t.getPoint (1) | |
t.getPoint (1) == t.getPoint (2) | |
t.getPoint (2) == t.getPoint (0))
continuar;
System.out.print ( "\ t \ t \ t");
if (t.isFrontFacing ())
System.out.print (t.getPoint (0) + ""
+ T.getPoint (1) + "" + t.getPoint (2));
59

VRML 2.0 con Java CAPÍTULO 13

else {
if (mdl.getTextureVertex (t.getPoint (0)). isOnseam ())
System.out.print ((t.getPoint (0)
Mdl.getNumberOfVertices + ()) + "");
algo más
System.out.print (t.getPoint (0) + "");
if (mdl.getTextureVertex (t.getPoint (1)). isOnseam ())
System.out.print ((t.getPoint (1) +
Mdl.getNumberOfVertices + ()) + "");
algo más
System.out.print (t.getPoint (1) + "");
if (mdl.getTextureVertex (t.getPoint (2)). isOnseam ())
System.out.print ((t.getPoint (2)
+ Mdl.getNumberOfVertices ()));
algo más
System.out.print (t.getPoint (2));
}
System.out.println ( "-1");
}
System.out.println ( "\ t \ t]");
}
System.out.println ( "\ t)"); / / fin de IndexedFaceSet
System.out.println ("}"); / / fin de Forma

Aviso de nuevo que vaya degenerar triángulos.

Dado que hay un uno-a-una correspondencia entre vértices y coordenadas de


textura, utilizamos los índices de los vértices de cada triángulo como la textura
coordinar índices. Al menos, eso es lo que hacemos por triángulos orientados
hacia delante. Para volver frente triángulos, comprobamos cada vértice para
ver si se encuentra en la costura, si es así, que el índice en la segunda serie de
coordenadas de textura (los que han skinwidth / 2 añade a ellos).

Una vez que la lista de índices de coordinar la textura es completa, estamos


terminado la construcción de nuestra forma. Ponemos a cabo la llave de cierre,
y terminado. Casi.

Generación de la animación

Hasta ahora hemos visto cómo generar una, textura versión de la criatura. ¿No
sería bueno hacer que se mueva?
60

VRML 2.0 con Java CAPÍTULO 13

Bueno, eso es exactamente lo que vamos a hacer. Como se recordará a partir


de nuestra primera discusión sobre el formato de archivo, animación en cada
uno MDL archivo contiene en realidad un grupo completamente distinto de los
vértices de coordenadas. Tenemos que mover esos vértices para animar los
marcos.

Afortunadamente, cuenta con un nodo de VRML que nos permite hacer


exactamente eso: la CoordinateInterpolator. Vamos a generar un nodo
CoordinateInter-polator para cada secuencia de animación de fotogramas clave
en la línea de demarcación militar de archivo, junto con un TimeSensor para
conducir y algunas rutas para conectar todo junto.

En primer lugar, comprobar para ver si estamos de generación de la animación,


que puede recordar es controlado por el pabellón en la línea de comandos. A
continuación, compruebe si esta cifra tiene varios grupos-marco, que no
estamos dispuestos a apoyar pero, si es así, sólo un mensaje de error y salida:

si (animación) {
System.out.println ( "])"); / / final del Grupo de nodo con TouchSensor
/ / Comprobar marco para múltiples grupos
for (int i = 0; i <mdl.getNumberOfFrameGroups (), i) {
if (mdl.getFrameGroup (i). getNumberOfSubFrames ()! = 1) {
System.err.println {
"lo siento, varios grupos que no apoyaron marco");
System.exit (2);
}
}

La identificación de las secuencias de animación

Marcos de la animación en la línea de demarcación militar archivos que se


envían con Quake tienen una propiedad interesante: Marcos que forman parte
de una única secuencia de todos tienen nombres similares, que consiste en la
actividad (tales como "caminando") seguido de un número de frame. Podemos
utilizar esa información para nuestro beneficio, a fin de crear
CoordinateInterpolator nodos separados para cada secuencia de animación.

Se define una nueva clase, llamada de secuencia, que se parece a esto:

Secuencia de la clase {
Cadena nombre protegidas;
protegidas int inicio, final;
61

VRML 2.0 con Java CAPÍTULO 13

Cadena getName () (return nombre;)


int getStart () (return iniciar;)
int getEnd () (return fin;)
vacío setEnd (int endvalue) (end = endvalue;)

Secuencia público (String namestr, int startvalue) {


nombre = new String (namestr);
start = startvalue;
}
}

Lo único que hace es llevar un registro de nombre de la secuencia (que es el


nombre de la imagen, sin el marco numérico trailing número), así como el
principio y fin de marcos dentro de la línea de demarcación militar de archivo
de la lista de cuadros.

De vuelta en nuestra línea principal, se comienza por la creación de una lista


de estas secuencias de animación:

Seqlist vector = new Vector ();


/ / Encontrar todas las secuencias
Secuencia = new siguientes Secuencia {
trimDigits (getFrame (MDL, 0). getName ()), 0);
for (int i = 1; i <mdl.getNumberOfFrameGroups (), i) {
String name = trimDigits (getFrame (MDL, i). GetName ());
if (! name.equals (seq.getName ())) (
seq.setEnd (i-1);
seqlist.addElement (siguientes);
Secuencia = new siguientes (nombre, i);
}
}
seq.setEnd (mdl.getNumberOfFrameGroups () -1);

Este código es un poco sutil, de modo que una explicación está en orden.
Sabemos que todos los grupos tienen un marco único conjunto de marcos
(desde que salió con un error que si no era cierto). Usamos un método especial
llamado getFrame () para acceder a las cero de un marco marco de grupo, ya
que es algo que tendrá que hacer mucho. Obtenemos el nombre del primer
cuadro, recorte cualquier trailing dígitos, y crear un nuevo marco de secuencia
a partir de cero.

A continuación, pasar por el resto de marcos. Tenga en cuenta que empezamos


en un marco, ya que ya hemos procesado marco cero. Para cada uno de los
cuadros, que recorte los dígitos de la denominación y comprobar que en contra
62

VRML 2.0 con Java CAPÍTULO 13

de la actual secuencia de su nombre. Si no coinciden, tenemos que empezar


una nueva secuencia. Actualizamos el final de la secuencia anterior a la
estructura antes de la actual, agrega la secuencia anterior a la del vector
llamado seqlist, y comenzar una nueva secuencia con el actual marco. Al final,
cerrar la última secuencia mediante el establecimiento de su marco a fin ser el
último cuadro.

Este es el trimDigits () el método que hemos usado anteriormente:

Cadena estática trimDigits (String str) {


for (int i = 0; i <str.length (); i)
if (Character.isDigit (str.charAt (i)))
volver str.substring (0, i);
str retorno;
}

Y aquí está la getFrame () método:

Marco getFrame estático (MDLFile MDL, int n) {


volver mdl.getFrameGroup (n). getSubFrame (0);
}

Eso es todo lo que a él.

La generación de los nodos CoordinateInterpolator

Ahora que sabemos el nombre de cada secuencia de animación y sus cuadros


de inicio y de fin, podemos generar el interpolador nodos.

Vamos a través del vector seqlist, y producir un CoordinateInter-polator para


cada entrada. Asignamos el nombre seguido de la secuencia _CI como el
nombre de la DEF CoordinateInterpolator:

para (e = Enumeración seqlist.elements (); e.hasMoreElements ();) {


Fr marco;
S = Secuencia (Secuencia) e.nextElement ();
System.out.println ( "DEF" +
s.getName () + "_CI CoordinateInterpolator (");
System.out.println ( "\ tkey [");
int nvalues = s.getEnd () - s.getStart () + 1;
for (int i = 0; i <= nvalues; + + i)
System.out.println ( "\ t \ t" + i / (float) nvalues);
System.out.println ( "\ t]");
63

VRML 2.0 con Java CAPÍTULO 13

System.out.println ( "\ tkeyValue [");


for (int i = 0; i <nvalues; + + i) {
fr = getFrame (MDL, s.getStart () + i);
for (int j = 0; j <mdl.getNumberOfVertices (); + + j)
emit_vertex (fr.getVertex (j), el MDL);
}
fr = getFrame (MDL, s.getStart ());
for (int j = 0; j <mdl.getNumberOfVertices (); + + j)
emit_vertex (fr.getVertex (j), el MDL);
System.out.println ( "\ t]");
System.out.println ("}");
}

Los principales valores son el índice dividido por el número de valores, en otras
palabras, si hay cinco cuadros, a continuación, las claves será 0 / 5, 1 / 5, 2 / 5,
3 / 5, 4 / 5, y 5 / 5 (o, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0).

Los valores son keyValue difícil. Para cada valor de clave, habrá tantos vértices
como hay vértices en la criatura, es decir, hay nvalues conjuntos de cada uno
de los vértices nvertices. Para cada uno de los cuadros a partir de la estructura
de esta secuencia (s.getStart ()) para nvalues cuadros, que emiten todos los
vértices (que habrá nvertices de ellos).

Después de terminado, que emiten el primer vértice de nuevo. Esto es


necesario para evitar un "salto" si la secuencia de bucles. Tenga en cuenta que
esto significa que el keyValue correspondiente a una clave de 0 / 5 será el
mismo que el keyValue correspondiente a una llave de 5 / 5.

Observe que una vez más el uso emit_vertex (), ya que se encarga de la
ampliación y la traducción para nosotros.
La generación de los nodos NormalInterpolator

Tenemos que generar un NormalInterpolator para cada CoordinateInterpolator,


ya que las normales cambiar de dirección como la criatura se mueve:

System.out.println ( "DEF" s.getName + () + "_NI NormalInterpolator (");


System.out.println ( "\ tkey [");
for (int i = 0; i <= nvalues; + + i)
System.out.println ( "\ t \ t" + i / (float) nvalues);
System.out.println ( "\ t]");
System.out.println ( "\ tkeyValue [");
for (int i = 0; i <nvalues; + + i) {
fr = getFrame (MDL, s.getStart () + i);
for (int j = 0; j <mdl.getNumberOfVertices (); + + j)
64

VRML 2.0 con Java CAPÍTULO 13

emit_normal (fr.getVertex (j), el MDL);


}
fr = getFrame (MDL, s.getStart ());
for (int j = 0; j <mdl.getNumberOfVertices (); + + j)
emit_normal (fr.getVertex (j), el MDL);
System.out.println ( "\ t]");
System.out.println ("}");

Este código es casi idéntica a lo que acabamos de examinar, salvo que una
tachuela en el interpolador _NI nombre en lugar de una _CI, y llame a
emit_normal () en lugar de emit_vertex ().
Generar el TimeSensor y Rutas

El CoordinateInterpolator y NormalInterpolator nodos son impulsados por un


TimeSensor. Generar es trivial:

System.out.println ( "DEF" s.getName + ()


+ "_TS TimeSensor (bucle FALSE cycleInterval 5 stopTime -1)");

Hemos elegido tener la animación de cada una duración de cinco segundos y


no bucle. Estos ajustes se pueden cambiar fácilmente, podría añadir una
opción de línea de comandos para ello, o simplemente dejar que el usuario
edite el archivo después de que se ha generado.

A continuación, tenemos que VÍA la salida de la TimeSensor interpolador a los


distintos nodos que hemos producido, y su producción VÍA Coordinar a la
normal y los nodos de la forma generamos anterior:

System.out.println ( "RUTA" s.getName + ()


+ "_TS.fraction_changed A"
S.getName + ()
+ "_CI.set_fraction");
System.out.println ( "RUTA" s.getName + ()
+ "_CI.value_changed A C.point");
System.out.println ( "RUTA" s.getName + ()
+ "_TS.fraction_changed A"
S.getName + () + "_NI.set_fraction");
System.out.println ( "RUTA"
S.getName + ()
+ "_NI.value_changed A N.vector");
System.out.println ();

Generación de Facultativo RUTAS


65

VRML 2.0 con Java CAPÍTULO 13

Y, por último, pasamos por algunos facultativos y generar rutas, una para cada
secuencia de animación. En cada caso, le VÍA TouchSensor que la vuelve a
añadir al principio de la la TimeSensor, de modo que al hacer clic sobre la
criatura hará que la secuencia. Dejamos hasta que el usuario descomentar
alguna de estas que son útiles y pegar un comentario frente a la omisión
TimeSensor nodo (s) de lo generado antes. El uso puede también, por
supuesto, cambiar el bucle de ajuste en cualquiera de los nodos para obtener
TimeSensor acción continua (especialmente útil para las acciones repetidas,
como caminar):

para (e = Enumeración seqlist.elements (); e.hasMoreElements ();) {


S = Secuencia (Secuencia) e.nextElement ();
System.out.println ( "# TOUCH.touchTime VÍA A"
S.getName + () + "_TS.startTime");
}

Y terminado.

Nota: debes descomentar TimeSensor uno de los nodos con el fin de obtener la
animación para trabajar.

Correr mdl2vrml

Podemos ejecutar mdl2vrml de la siguiente manera:

java mdl2vrml somefile.mdl> somefile.wrl

java-mdl2vrml un somefile.mdl> somefile.wrl

si queremos animaciones. Tenga en cuenta que las animaciones que el archivo


de salida mucho, mucho más grande. También podemos generar una textura
mapeados versión:

java mdl2vrml-t somefile.mdl> somefile.wrl

que también daría lugar a una o más de Windows. archivos BMP, que contiene
los mapas de texturas. Y, por supuesto, podría usar el comando

java-mdl2vrml un somefile.mdl-t> somefile.wrl

si queremos que ambos mapas de texturas y animación.


66

VRML 2.0 con Java CAPÍTULO 13

Figura 13-2 muestra una típica criatura sin el mapeo de texturas, y la Figura
13.3 muestra que con el mapeo de texturas.

Figura 13.2 Una llanura viejo demonio

Figura 13.3 Una textura mapeados demonio

Conclusiones

El Quake MDL convertidor que hemos presentado aquí es bastante completa. Como se
mencionó anteriormente, se puede modificar para producir un nodo MovieTexture cuando
un multipicture piel se encuentra, y usted podría incluso intentar hacer frente a grupos
marco que contengan más de un cuadro (aunque son muy raros, y probablemente no vale la
pena).

Una vez más, un recordatorio de que las criaturas son Quake propiedad de id Software, lo
que no se puede utilizar sin permiso. Sin embargo, si los demás usuarios crear servicios
para la producción de Quake criaturas, se le puede convertir a VRML, con mapas de
texturas y animación.

Animación Master SEG

Animación Maestro es un paquete de modelado y animación de hash Incorporated


(http://www.hash.com/). Está basada en spline parches en lugar de polígonos, que tiene
algunas ventajas cuando se trata de modelado de formas orgánicas. Splines también son
potencialmente más eficaz, a pesar de que tomar mucho más tiempo para hacer. El paquete
es bastante potente y muy asequible, pero es difícil de utilizar debido a su escasa
documentación. También la versión 4.07 se bloquea de forma regular, que puede ser
frustrante.

Animación Master utiliza un gran número de archivos diferentes para describir una figura.
Algunas de ellas están documentadas, pero, de nuevo, la documentación es muy limitado y
67

VRML 2.0 con Java CAPÍTULO 13

contiene algunos errores. En equidad a los autores de las especificaciones, que están
claramente destinados a ser no oficial "sugerencias" para los programadores más que parte
del producto en sí.

En esta sección vamos a limitarnos a la SEG el formato de archivo, que es producida por el
módulo de Escultura de la animación Maestro. Un segmento es una descripción
geométricas de una forma y se compone de una serie de splines.

Formato de archivo

La información sobre el formato de archivo SEG proviene de un documento publicado por


hash titulado "Dentro de los formatos de archivo." La SEG formato ASCII y orientado a la
línea. SEG archivos tienen una extensión de archivo de. SEG, para "segmento".

El fichero comienza con una línea de cabecera, cuya primera cuatro personajes son SCUL
(para "escultura"). Estos personajes son seguidos por el número de versión, y luego un cero
y, a continuación, una plataforma de tipo bandera y otros tres ceros. El tipo de plataforma
es de 1 o 2 para Windows para PowerMac, las dos plataformas en las que la animación
Master está disponible. El número de versión es importante, ya que el formato de archivo
ha cambiado de una versión maestra de la animación a otra.

Tras la cabecera de línea es una línea que contiene un entero llamado segmento de
Identificación, que (no es sorprendente) se utiliza para identificar el segmento. La siguiente
línea contiene seis números de punto flotante que es el mínimo y máximo x, y, z y los
valores (en efecto, la caja de la serie de sesiones). Esto es seguido por una línea que
contenga la x, y y z de los valores de punto de giro de la serie de sesiones, y una línea que
contenga el número de splines.

Los splines siga estos cabecera líneas. Cada spline comienza con una línea que contenga un
recuento del número de puntos de control en la spline, seguido de los puntos de control
propios.

Según la documentación, cada punto de control debe comenzar con el punto de control id
(CPID). Sin embargo, el examen de este archivo se contradice, sino que parece que la línea
contiene el tipo y adjuntar banderas, seguido por el CPID. Aunque no dejó claro en la
documentación, parece que los tres valores se almacenan en una sola línea.

El pabellón es un tipo lógico OR de dos banderas: LISA (0x01) y LOOP (0x04). El buen
bandera indica que este punto de control debe ser suave en lugar de ser un ángulo agudo. El
LOOP bandera indica que este punto debería volver al circuito el primer punto de control
de la spline, que se utiliza generalmente (en su caso) en el último punto de control de la
spline.

Si concedemos la bandera tiene un valor distinto de cero, indica que este punto de control
está conectado a otro punto de control. Si este es el caso, la siguiente línea del archivo
contiene el CPID el punto de que este punto se adjunta a la. Si la bandera es igual a cero, la
siguiente línea contiene la x, y, z y los valores de este punto de control.
68

VRML 2.0 con Java CAPÍTULO 13

Esto es seguido por una línea que contenga la outalpha, outgamma, y outmag valores.
Outalpha Si el valor es cero, entonces outalpha por defecto a 100 y el outgamma y outmag
se ignoran y los valores por defecto a cero. Esto se repite con una línea que contenga la
inalpha, ingamma, inmag y valores, que por defecto de la misma manera.

Factores que complica

Analizar los archivos de la SEG es fácil, pero hay muchos problemas en la conversión en
VRML. Todos estos desafíos se derivan de la falta de documentación sobre el formato de
archivo.

El alfa, gamma, y la magnitud valores tienen que ver con la curva de spline, pero la
documentación no explica qué tipo de spline se utiliza o cómo hacer la interpolación spline.
También es claro que no sólo la documentación de la forma en que el splines deben estar
conectados para formar parches. Esto hace que sea difícil teselar ellos, o incluso
convertirlos en polígonos.

A falta de documentación adicional, lo mejor que podemos hacer es convertir los puntos de
control en los vértices y splines en segmentos de línea.

La aplicación de la SEG Parser

Usaremos el mismo enfoque aquí como lo hicimos para el NFF y MDL analizadores. Que
habrá una clase para cada uno de los principales tipos de datos en un archivo SEG: Spline ",
ControlPoint, y del segmento. También existe una clase Vec3f utilidad.

La clase Vec3f

Vec3f la clase es similar a la que utiliza para analizar el NFF:

/ / Una simple clase de vectores 3D para analizar efectos de animación Maestro

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete de hachís;

importación java.io. *;
importación java.util .*;

clase pública Vec3f {


protegidas flotar x = 0, y = 0, z = 0;

flotador público getX () (return x;)


flotador público Gety () (return y;)
flotador público Getz () (return z;)

public String toString () (return x "" y "" z;)

público Vec3f (String s) {


69

VRML 2.0 con Java CAPÍTULO 13

StringTokenizer tokens = new StringTokenizer (s);


x = Float.valueOf (tokens.nextToken ()). floatValue ();
y = Float.valueOf (tokens.nextToken ()). floatValue ();
z = Float.valueOf (tokens.nextToken ()). floatValue ();
}
}

El segmento de la clase

Este debe estar familiarizado territorio por ahora. El segmento de clase de


tiendas de la versión, tipo de plataforma, el segmento de identificación, de
pivote, caja, y una serie de splines:

/ / Un AnimationMaster Segmento

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete de hachís;

importación java.io. *;
importación java.util .*;

Segmento público de clase {


int versión protegidas;
int platformType protegidas;
int segmentId protegidas;
Flotador minX protegidas, miny, minz, maxx, Maxy, maxz;
Flotador pivotX protegidas, pivotY, pivotZ;
int nsplines protegidas;
Spline "protegidas [] splines;

público int getVersion () (return versión;)


público int getPlatformType () (return platformType;)
público int getSegmentId () (return segmentId;)
flotador público getPivotX () (return pivotX.floatValue ();)
flotador público getPivotY () (return pivotY.floatValue ();)
flotador público getPivotZ () (return pivotZ.floatValue ();)
público int getNumberOfSplines () (return nsplines;)
Spline "getSpline público (int n) (return splines [n];)

ControlPoint findControlPoint público (int cpid) {


for (int i = 0; i <nsplines, i) {
70

VRML 2.0 con Java CAPÍTULO 13

S = spline splines [i];


for (int j = 0; j <s.getNumberOfControlPoints ();
j) {
if (s.getControlPoint (j). getCPID () == cpid)
volver s.getControlPoint (j);
}
}
return null;
}

Segmento público (DataInputStream entrada)


lanza IOException, HashSyntaxException {
StringTokenizer s;
s = new StringTokenizer (input.readLine ());
if (s.countTokens ()! = 7)
lanzar nuevos HashSyntaxException {
"campos en los que falta línea de cabecera");
if (s.nextToken (). es igual a ( "SCUL") == false)
lanzar nuevos HashSyntaxException {
"falta la firma en primera línea");
version = Integer.parseInt (s.nextToken ());
s.nextToken (); / / saltar cero
platformType = Integer.parseInt (s.nextToken ());

segmentId = Integer.parseInt (input.readLine ());


s = new StringTokenizer (input.readLine ());
if (s.countTokens ()! = 6)
lanzar nuevos HashSyntaxException {
"faltan los campos en el segmento límites");
minX = nuevo flotador (s.nextToken ());
miny = new flotador (s.nextToken ());
minz = new flotador (s.nextToken ());
maxx = new flotador (s.nextToken ());
Maxy = nuevo flotador (s.nextToken ());
maxz = new flotador (s.nextToken ());

s = new StringTokenizer (input.readLine ());


if (s.countTokens ()! = 3)
lanzar nuevos HashSyntaxException {
"faltan los campos en el segmento de pivote línea");
pivotX = new flotador (s.nextToken ());
pivotY = new flotador (s.nextToken ());
pivotZ = new flotador (s.nextToken ());
71

VRML 2.0 con Java CAPÍTULO 13

nsplines = Integer.parseInt (input.readLine ());


splines = new Spline "[nsplines];
for (int i = 0; i <nsplines; i)
splines [i] = new Spline (entrada);
}
}

El constructor analiza la cabecera líneas Spline "y utiliza el constructor de la


clase para leer cada spline.

La clase Spline "

Spline "La clase es trivial, sino que lee un recuento del número de puntos de
control y, a continuación, utiliza el constructor ControlPoint leer cada punto de
control:

/ / Una curva AnimationMaster Spline "

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete de hachís;

importación java.io. *;

clase pública Spline "{


int npoints protegidas;
ControlPoint protegidas [] puntos;

público int getNumberOfControlPoints () (return npoints;)


ControlPoint getControlPoint público (int n) {
retorno puntos [n];
}

público Spline (DataInputStream entrada)


lanza IOException, HashSyntaxException {
npoints = Integer.parseInt (input.readLine ());
puntos = new ControlPoint [npoints];
for (int i = 0; i <npoints; i)
puntos [i] = new ControlPoint (entrada);
}

}
72

VRML 2.0 con Java CAPÍTULO 13

La clase ControlPoint

ControlPoint La clase almacena la cpid para el punto, tres Booleanos (el suave,
lazo, y adjuntar los pabellones) y seis valores spline (inalpha, ingamma, inmag,
outalpha, outgamma, y outmag). Los campos se han facilitado a los CPID en el
punto de control que está adjunta a, así como la x, y, z ubicación de este
punto, sólo uno de los dos realmente se utiliza para cualquier punto de control:

/ / Un solo punto de control AnimationMaster spline

/ / Escrito por Bernie Roehl, noviembre de 1996

paquete de hachís;

importación java.io. *;
importación java.util .*;

clase pública ControlPoint {


int cpid protegidas;
boolean buen protegidas;
boolean bucle protegidas;
boolean adjuntar protegidas;
int attached_cpid protegidas;
protegidas doble x, y, z;
protegidas outalpha doble, outgamma, outmag;
protegidas inalpha doble, ingamma, inmag;

público int getCPID () (return cpid;)


público isSmooth boolean () (return suave;)
público isLooped boolean () (return lazo;)
público isAttached boolean () (return adjuntar;)
público int getAttachedCPID () (return attached_cpid;)
público getX doble () (return x;)
público Gety doble () (return y;)
público doble Getz () (return z;)

ControlPoint público (DataInputStream entrada)


lanza IOException, HashSyntaxException {
StringTokenizer s;

s = new StringTokenizer (input.readLine ());


if (s.countTokens ()! = 3)
lanzar nuevos HashSyntaxException {
"faltan los campos de cabecera spline punto");
73

VRML 2.0 con Java CAPÍTULO 13

int tipo = Integer.parseInt (s.nextToken ());


buen = ((tipo y 0x01)! = 0)? verdadero: falso;
loop = ((tipo y 0x04)! = 0)? verdadero: falso;
adjuntar = (Integer.parseInt (s.nextToken ())! = 0)
? verdadero: falso;
cpid = Integer.parseInt (s.nextToken ());
if (adjuntar)
attached_cpid =
Integer.parseInt (input.readLine ());
else {
s = new StringTokenizer (input.readLine ());
if (s.countTokens ()! = 3)
lanzar nuevos HashSyntaxException {
"campos en los que faltan spline punto x, y, z la línea");
x = Float.valueOf (s.nextToken ()). floatValue ();
y = Float.valueOf (s.nextToken ()). floatValue ();
z = Float.valueOf (s.nextToken ()). floatValue ();
}

s = new StringTokenizer (input.readLine ());


outalpha = (nuevo flotador (s.nextToken ())). floatValue ();
outmag = 100 / / valor por defecto
if (s.hasMoreTokens ()) {
outgamma =
Float.toValue ((s.nextToken ()). FloatValue ();
if (s.hasMoreTokens ())
outmag =
Float.toValue ((s.nextToken ()). FloatValue ();
}

s = new StringTokenizer (input.readLine ());


inalpha =
Float.toValue ((s.nextToken ())). floatValue ();
inmag = 100 / / valor por defecto
if (s.hasMoreTokens ()) {
ingamma =
Float.toValue ((s.nextToken ())). floatValue ();
if (s.hasMoreTokens ())
inmag =
Float.toValue ((s.nextToken ())). floatValue ();
}
}
}
74

VRML 2.0 con Java CAPÍTULO 13

El StringTokenizer de Java y del manejo de excepciones simplificar este tipo de


operaciones de análisis sintáctico.

Generación de la salida de VRML

Como se indicó anteriormente, no hay suficiente información en el formato de


archivo de documentación para el proceso de spline parches, o incluso los
propios splines. A falta de más información, todo lo que podemos hacer es
utilizar los puntos de control como vértices y gire a la splines en líneas
mediante la generación de un IndexedLineSet.

Empezamos por la generación de una cabecera, un nodo de transformación a


la forma de posición en relación con su pivote, y el inicio de una forma nodo. A
continuación, generar un nodo Apariencia y el inicio de la IndexedLineSet:

/ / Leer un archivo de animación Maestro SEG y de exportación como de VRML

/ / Escrito por Bernie Roehl, noviembre de 1996

importación java.io. *;
importación java.util .*;
importación hash .*;

clase pública scul2wrl {


public static void main (String args []) {
try {
Segmento Segmento nuevo seg = {
nuevo DataInputStream (System.in));
System.out.println ( "# VRML V2.0 utf8");
System.out.println ( "\ n # creación de SCUL2WRL \ n");
System.out.println ( "Transformar (");
System.out.println ( "\ ttranslation"
Seg.getPivotX + () + ""
Seg.getPivotY + () + ""
Seg.getPivotZ + ());
System.out.println ( "\ tchildren [");
System.out.println ( "\ t \ (tShape");
System.out.println {
"\ t \ t \ (tappearance Apariencia
material Material (diffuseColor 1 1 1)) ");
System.out.println ( "\ t \ t \ tgeometry IndexedLineSet (");
75

VRML 2.0 con Java CAPÍTULO 13

La emisora de Vértices

Ahora vamos a través de todos los splines, y todos los puntos de control para
cada uno de los splines. Para cualquier punto de control que no está conectada
a otro (es decir, para cada uno que es un "real" en lugar de un punto de volver
a utilizar uno), almacenamos una referencia al punto de control de vectores en
una llamada coordenadas:

/ / Se reúnen los vértices


Vector de coordenadas = new Vector ();
for (int i = 0; i <seg.getNumberOfSplines (); + + i) {
Spline spline = seg.getSpline (i);
for (int j = 0; j <spline.getNumberOfControlPoints (); j + +) {
ControlPoint cp = spline.getControlPoint (j);
if (cp.isAttached () == false)
coordinates.addElement (pc);
}
}

El siguiente paso es emitir todos estos puntos de control único como vértices,
con un nodo de coordenadas:

/ / Y emiten ellos
System.out.println ( "\ t \ t \ t \ tcoord Coordinar (");
System.out.println ( "\ t \ t \ t \ t \ tpoint [");
for (int k = 0; k <coordinates.size (); k + +) (
ControlPoint pt = (ControlPoint) coordinates.elementAt (k);
System.out.println ( "\ t \ t \ t \ t \ t \ t"
Pt.getX + () + "" + pt.getY () + "" + pt.getZ ()
+ "#" + Pt.getCPID ());
}
System.out.println ( "\ t \ t \ t \ t \ t]");
System.out.println ( "\ t \ t \ t \ t)");

Poner fuera de la línea Segmentos

La emisión de las líneas propias, vamos a través de los splines de uno en uno. Para cada
spline, corremos a través de los puntos de control. Si un punto se marca como adjunto,
esperamos hasta el punto que se adjunta a si este punto también es marcado como adjunto,
esperamos hasta el punto que se adjunta, y así sucesivamente. Esto es algo así como una
lista siguiente.
76

VRML 2.0 con Java CAPÍTULO 13

Una vez que hayamos encontrado un punto solteras, esperamos que en las coordenadas
matriz, que se corresponde uno a uno con el conjunto de vértices de coordenadas en el nodo
que emiten antes. El índice en la matriz se emite el siguiente punto a lo largo de la línea que
estamos generando. Si es el primer punto, hacemos un seguimiento de lo que tenemos en el
caso del ciclo al final de la spline:

System.out.println ( "\ t \ t \ t \ tcoordIndex [");


for (int i = 0; i <seg.getNumberOfSplines (); + + i) {
Spline spline = seg.getSpline (i);
int firstpoint = 0;
boolean lastlooped = false;
System.out.print ( "\ t \ t \ t \ t \ t \ t");
for (int j = 0; j <spline.getNumberOfControlPoints (); j + +) {
ControlPoint cp = spline.getControlPoint (j);
lastlooped = cp.isLooped ();
while (cp.isAttached ())
cp = seg.findControlPoint (cp.getAttachedCPID ());
int n = coordinates.indexOf (pc);
System.out.print (n + "");
if (j == 0)
firstpoint = n;
}
if (lastlooped)
System.out.print (firstpoint + "");
System.out.println ( "-1");
}
System.out.println ( "\ t \ t \ t \ t]");
System.out.println ( "\ t \ t \ t)");
System.out.println ( "\ t \ t)");
System.out.println ( "\ t]");
System.out.println ("}");

Cuando llegamos a la final de la spline, ponemos un -1 para indicar el final de esta serie de
líneas.

El manejo de la bandera lastlooped merece algunas explicaciones. Cada spline puede


producir ya sea una línea abierta o cerrada (es decir, que de nuevo se cierra sobre sí
mismo). El pabellón lastlooped está inicialmente definido en falso, pero los cambios en su
entorno el valor de cada punto de control en ese punto se procesa. Esto significa que
cuando lleguemos al final de la spline, el lastlooped pabellón corresponde al valor de la
bandera de bucle en el último punto de control en la spline. Si el indicador está activado
cuando se llega a la final de la spline, volvemos a emitir el índice del primer punto de la
spline, a fin de cerrar esta opción.

Eso es todo. Para procesar un archivo SEG, corre mdl2vrml, de la siguiente manera:

java mdl2vrml <infile.seg> outfile.wrl


77

VRML 2.0 con Java CAPÍTULO 13

Tenga en cuenta que se lee de su entrada estándar en lugar de tomar un nombre de archivo
en la línea de comandos.

El jefe de una figura humanoide que ha sido convertido mediante mdl2vrml se muestra en
la Figura 13.4.

Figura 13.4 Un alambre cabeza

You might also like