O tema é relevante tanto para desenvolvedores que desejam ampliar e tornar mais interessantes os cenários de interação de suas aplicações, quanto para aqueles que desejam desenvolver animações e jogos com interfaces gráficas tridimensionais.
Em aplicações que fazem uso de recursos tridimensionais, é muitas vezes desejável que os objetos apresentem aparência semelhante a aquela apresentada na realidade. Neste artigo, serão abordados recursos e classes da API Java 3D que permitem o carregamento de modelos tridimensionais, a definição de fontes de luz e a configuração da aparência de objetos.
Neste artigo, serão estudados recursos relacionados ao carregamento de modelos tridimensionais e a aparência de geometrias, bem como será iniciado o desenvolvimento de um jogo.
Carregamento de Modelos Tridimensionais
O primeiro artigo apresentou duas formas de definição de objetos geométricos: através do uso de classes utilitárias, que definem primitivas geométricas – cilindro, esfera, cubo, entre outras –, e a partir da definição de vértices e polígonos.
A definição de objetos complexos através de qualquer uma destas duas formas pode ser difícil e trabalhosa. Por esse motivo, o uso de ferramentas de modelagem 3D, como o 3ds Max ou o Blender, é de grande valor para a produção de modelos complexos.
Em Java 3D, modelos tridimensionais podem ser utilizados através de carregadores (loaders), classes responsáveis por importar objetos criados em ferramentas de modelagem para o universo virtual, tais como Wavefront (.obj), 3ds Max (.3ds) e VRML (.vrml).
Para permitir que, com o surgimento de novos tipos de arquivos de modelos, possam ser criados carregadores apropriados, a API define interfaces (pacote com.sun.j3d.loaders) que especificam o comportamento comum a carregadores. Este pacote define duas interfaces: Loader e Scene.
A interface Loader especifica os métodos responsáveis pela leitura, análise e criação do grafo de conteúdo associado ao objeto tridimensional, enquanto a interface Scene armazena os dados lidos pelo loader. Para facilitar a criação de carregadores, é fornecida, neste pacote, uma classe abstrata que implementa alguns métodos da interface Loader.
A seguir, será apresentado um exemplo de carregamento de arquivos Wavefront (.obj) através da classe ObjectFile, que é distribuída no pacote com.sun.j3d.loaders.objectfile.
O método load(), especificado pela interface Loader e implementado por esta classe, é sobrecarregado e possui três versões. Caso seja utilizada a versão que recebe como parâmetro um Reader e a leitura seja feita a partir de um arquivo localizado em uma pasta diferente daquela onde se encontra a classe, o arquivo .mtl – de aparência do objeto Wavefront, que acompanha o .obj – não será encontrado.
Isto ocorre porque, no código-fonte da classe ObjectFile, existem atributos que armazenam a pasta base onde estão os arquivos .obj e .mtl. Entretanto, esse atributo só é definido para os construtores que recebem URL ou String, não para o construtor que recebe um Reader. Assim sendo, no uso deste último, deve-se chamar o método setBasePath() para definir a pasta onde se encontra o arquivo .mtl e suas texturas.
O código apresentado na Listagem 1 carrega e exibe um modelo do satélite TDRS da NASA, copiado de http://www.nasa.gov/multimedia/3d_resources/models.html. O arquivo copiado está no formato .3ds e foi convertido para .obj, através do Blender, para que o carregador do Wavefront pudesse ser utilizado.
package quadrans.java3d;
// imports…
public class CarregarModelo1 {
public static void main(String[] args) {
SimpleUniverse universo = new SimpleUniverse(); // Cria um universo virtual.
// Cria um subgrafo de conteúdo.
BranchGroup subgrafoConteudo = new BranchGroup();
try { // Carrega o satélite e o adiciona ao universo.
ObjectFile carregador = new ObjectFile(ObjectFile.RESIZE);
carregador.setBasePath("c://Modelo3D");
Scene cena = carregador.load(new FileReader("c://Modelo3D//TDRS.obj"));
subgrafoConteudo.addChild(cena.getSceneGroup());
universo.addBranchGraph(subgrafoConteudo);
} catch(Exception erro) { erro.printStackTrace(); }
// Define o comportamento do observador.
OrbitBehavior comportamentoOrbita = new OrbitBehavior(universo.getCanvas(),
OrbitBehavior.REVERSE_ALL);
comportamentoOrbita.setSchedulingBounds(new BoundingSphere());
universo.getViewingPlatform().setViewPlatformBehavior(comportamentoOrbita);
universo.getViewingPlatform().setNominalViewingTransform();
}
}
As linhas de código em negrito são responsáveis por carregar o modelo e o adicionar ao universo virtual. Além dessas linhas, é adicionado um comportamento de órbita para que o usuário possa utilizar o mouse para a navegação no universo, conforme estudado no primeiro artigo. A Figura 1 exibe o resultado da execução desse código.
Os dados carregados por um loader podem conter tanto nós com objetos Shape3D, quanto comportamentos. Para acessá-los, pode-se utilizar o nome dos nós presentes na cena. A classe Scene possui um método chamado getNamedObjects(), que retorna uma lista de todos os nomes de objetos presentes na cena e os objetos associados no grafo de cena.
Para ilustrar o uso desse método, a Listagem 2 apresenta um segundo exemplo de carregamento de modelo, um carro, cuja roda é recuperada e rotacionada, criando a ideia de movimento (Figura 2). Para este exemplo, foi necessário definir fontes de luz, uma vez que o modelo não possui texturas e, sem elas, não seria possível visualizá-lo. Mais adiante, neste artigo, conceitos e técnicas sobre iluminação serão abordados.
package quadrans.java3d;
// imports...
public class CarregarModelo2 {
public static void main(String[] args) {
SimpleUniverse universo = new SimpleUniverse(); // Cria um universo virtual.
// Cria um subgrafo de conteúdo.
BranchGroup subgrafoConteudo = new BranchGroup();
// Define uma região de influência que será utilizada por recursos de iluminação.
BoundingSphere regiao = new BoundingSphere(new Point3d(0, 0, 0), 100);
// Carrega o satélite e o adiciona ao universo.
try {
ObjectFile carregador = new ObjectFile(ObjectFile.RESIZE);
carregador.setBasePath("c://Modelo3D");
Scene cena = carregador.load(new FileReader("c://Modelo3D//Nissan.obj"));
BranchGroup grupoCena = cena.getSceneGroup();
// Recupera uma referência para o Shape3D que representa a roda do carro.
String nomeRoda = "entity124";
grupoCena.removeChild(((Shape3D)cena.getNamedObjects().get(nomeRoda)));
// Cria um grupo de transformação para rotacioná-la.
TransformGroup grupoTransformacao = new TransformGroup();
grupoTransformacao.addChild(((Shape3D)cena.getNamedObjects().get(nomeRoda)));
grupoTransformacao.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
subgrafoConteudo.addChild(grupoTransformacao);
// Recupera os dois limites da roda para descobrir
// seu centro e, consequentemente, o eixo de rotação.
BoundingBox fronteiras = (BoundingBox)
((Shape3D)cena.getNamedObjects().get(nomeRoda)).getBounds();
Point3d superior = new Point3d();
fronteiras.getUpper(superior);
Point3d inferior = new Point3d();
fronteiras.getLower(inferior);
// Define o eixo de rotação
Transform3D eixoRotacao = new Transform3D();
eixoRotacao.rotZ(Math.toRadians(-90));
eixoRotacao.setTranslation(new Vector3d(0,
inferior.getY()+(superior.getY()-inferior.getY())/2,
inferior.getZ()+(superior.getZ()-inferior.getZ())/2));
// Cria um interpolador para rotacionar a roda.
RotationInterpolator interpoladorRotacao = new
RotationInterpolator(new Alpha(-1, 3000), grupoTransformacao);
interpoladorRotacao.setSchedulingBounds(regiao);
interpoladorRotacao.setTransformAxis(eixoRotacao);
subgrafoConteudo.addChild(interpoladorRotacao);
subgrafoConteudo.addChild(grupoCena);
} catch(Exception erro) { erro.printStackTrace(); }
// Define duas fontes de iluminação no universo virtual.
AmbientLight luzAmbiente = new AmbientLight(new Color3f(0.25f, 0.25f, 0.25f));
luzAmbiente.setInfluencingBounds(regiao);
subgrafoConteudo.addChild(luzAmbiente);
DirectionalLight luzDirecional1 =
new DirectionalLight(new Color3f(.8f, .8f, .8f), new Vector3f(-1f, 1f, -1f));
luzDirecional1.setInfluencingBounds(regiao);
subgrafoConteudo.addChild(luzDirecional1);
universo.addBranchGraph(subgrafoConteudo);
// Define o comportamento do observador.
OrbitBehavior comportamentoOrbita = new OrbitBehavior(universo.getCanvas(),
OrbitBehavior.REVERSE_ALL);
comportamentoOrbita.setSchedulingBounds(new BoundingSphere());
universo.getViewingPlatform().setViewPlatformBehavior(comportamentoOrbita);
universo.getViewingPlatform().setNominalViewingTransform();
}
}
...
Confira outros conteúdos:
Introdução ao JDBC
Novidades do Java
Teste unitário com JUnit
Black November
Desconto exclusivo para as primeiras 200 matrículas!
Pagamento anual
12x no cartão
De: R$ 69,00
Por: R$ 54,90
Total: R$ 658,80
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- 12 meses de acesso
Pagamento recorrente
Cobrado mensalmente no cartão
De: R$ 79,00
Por: R$ 54,90 /mês
Total: R$ 658,80
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- Fidelidade de 12 meses
- Não compromete o limite do seu cartão
<Perguntas frequentes>
Nossos casos de sucesso
Eu sabia pouquíssimas coisas de programação antes de começar a estudar com vocês, fui me especializando em várias áreas e ferramentas que tinham na plataforma, e com essa bagagem consegui um estágio logo no início do meu primeiro período na faculdade.
Estudo aqui na Dev desde o meio do ano passado!
Nesse período a Dev me ajudou a crescer muito aqui no trampo.
Fui o primeiro desenvolvedor contratado pela minha
empresa. Hoje eu lidero um time de desenvolvimento!
Minha meta é continuar estudando e praticando para ser um
Full-Stack Dev!
Economizei 3 meses para assinar a plataforma e sendo sincero valeu muito a pena, pois a plataforma é bem intuitiva e muuuuito didática a metodologia de ensino. Sinto que estou EVOLUINDO a cada dia. Muito obrigado!
Nossa! Plataforma maravilhosa. To amando o curso de desenvolvimento front-end, tinha coisas que eu ainda não tinha visto. A didática é do jeito que qualquer pessoa consegue aprender. Sério, to apaixonado, adorando demais.
Adquiri o curso de vocês e logo percebi que são os melhores do Brasil. É um passo a passo incrível. Só não aprende quem não quer. Foi o melhor investimento da minha vida!
Foi um dos melhores investimentos que já fiz na vida e tenho aprendido bastante com a plataforma. Vocês estão fazendo parte da minha jornada nesse mundo da programação, irei assinar meu contrato como programador graças a plataforma.
Wanderson Oliveira
Comprei a assinatura tem uma semana, aprendi mais do que 4 meses estudando outros cursos. Exercícios práticos que não tem como não aprender, estão de parabéns!
Obrigado DevMedia, nunca presenciei uma plataforma de ensino tão presente na vida acadêmica de seus alunos, parabéns!
Eduardo Dorneles
Aprendi React na plataforma da DevMedia há cerca de 1 ano e meio... Hoje estou há 1 ano empregado trabalhando 100% com React!
Adauto Junior
Já fiz alguns cursos na área e nenhum é tão bom quanto o de vocês. Estou aprendendo muito, muito obrigado por existirem. Estão de parabéns... Espero um dia conseguir um emprego na área.
Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.