LIBGDX APIMoverModelos3D
UNIDADE 5: Mover modelos 3D
Introdución
Neste apartado imos ver como mover os modelos 3D cargados coa clase ModelInstance, facendo uso das matrices.
Para mover os modelos só temos que aplicar o aprendido na Unidade 4.
Preparación:
Creamos unha nova clase de nome EX_3_MoverModelos que sexa chamada pola clase PracticasNovaApi3D.
Movendo os Modelos 3D
A matriz de modelado-vista se atopa na propiedade transform do ModelInstance, e este a su vez está asociado a un Model.
Clase utilizada: Matrix4.
Operacións (todas elas sobrecargadas) sobre a propiedade transform do ModelInstance:
- Traslación:
- public Matrix4 setToTranslation(float x,float y, float z): Carga a matriz identidade e posiciona o obxecto nas coordenadas indicadas. Esta sería a forma que deberíamos utilizar se seguimos o esquema Modelo-Vista-Controlador.
- public Matrix4 setTranslation(float x,float y, float z): Posiciona o obxecto nas coordenadas indicadas. Non carga a matriz identidade.
- Rotación:
- public Matrix4 setToRotation(Vector3 axis, float degrees): Carga a matriz de identidade e rota a matriz no ángulo indicado en grados e no eixe que teña o valor 1.
- public Matrix4 rotate(float axisX,float axisY,float axisZ,float degrees): Rota a matriz no ángulo indicado en grados e no eixe que teña o valor 1.
- Escala:
- public Matrix4 setToScaling(Vector3 vector): Carga a matriz identidade e escala a matriz no valor indicado no eixe correspondente.
- Atención:: Se usamos este método estaremos perdendo a información gardada na matriz de modelado. Para aplicar unha traslación e escala teremos que aplicar o método seguinte:
- Traslación e escala:
- public Matrix4 setToTranslationAndScaling(float translationX,float translationY,float translationZ,float scalingX,float scalingY,float scalingZ): Rota e escala á vez. Carga a matriz de identidade e aplica a rotación e escala nos eixes indicados cun valor de 1.
Imos facer un exemplo.
Seguindo co exercicio anterior (o podedes gardar facendo copy e paste sobre o propio arquivo en Eclipse).
Código da clase EX_3_MoverModelos
Obxectivo: Cargar varios modelos e animalos. Operacións de escala, rotacion e traslación.
1 import com.badlogic.gdx.Gdx;
2 import com.badlogic.gdx.Screen;
3 import com.badlogic.gdx.assets.AssetManager;
4 import com.badlogic.gdx.graphics.GL20;
5 import com.badlogic.gdx.graphics.PerspectiveCamera;
6 import com.badlogic.gdx.graphics.g3d.Environment;
7 import com.badlogic.gdx.graphics.g3d.Model;
8 import com.badlogic.gdx.graphics.g3d.ModelBatch;
9 import com.badlogic.gdx.graphics.g3d.ModelInstance;
10 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
11 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
12 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
13 import com.badlogic.gdx.utils.Array;
14
15
16
17 public class EX_3_MoverModelos implements Screen {
18
19 private PerspectiveCamera camara3d;
20 private ModelBatch modelBatch;
21
22 private Array<ModelInstance> instances;
23 private ModelInstance instanceNave;
24
25
26 private Environment environment;
27 private CameraInputController camController;
28
29 private float pos;
30
31 public EX_3_MoverModelos(){
32 camara3d = new PerspectiveCamera();
33 camController = new CameraInputController(camara3d);
34 Gdx.input.setInputProcessor(camController);
35
36 modelBatch = new ModelBatch();
37 environment = new Environment();
38 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
39 environment.add(new DirectionalLight().set(1f, 1f, 1f, 1f, 0f, 0f));
40
41 instances = new Array<ModelInstance>();
42
43 AssetManager assets = new AssetManager();
44 assets.load("ship.obj", Model.class);
45 assets.load("cube.g3db", Model.class);
46 assets.finishLoading();
47
48 Model modelNave = assets.get("ship.obj", Model.class);
49 Model modelCubo = assets.get("cube.g3db", Model.class);
50
51 instanceNave = new ModelInstance(modelNave);
52 instanceNave.transform.setToRotation(0, 1, 0, 90);
53
54 for (int cont=1; cont < 10;cont++){
55 ModelInstance cubo = new ModelInstance(modelCubo);
56 if (cont>3 && cont<6){
57 cubo.transform.setToTranslationAndScaling(cont*3, 0, 0, 0.5f, 0.5f, 0.5f);
58 }
59 else
60 cubo.transform.setTranslation(cont*3,0,0);
61
62 instances.add(cubo);
63
64 }
65
66 }
67
68
69 @Override
70 public void render(float delta) {
71 // TODO Auto-generated method stub
72 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
73
74 camController.update();
75
76 pos+=delta * 1f;
77
78 for (ModelInstance modelI : instances){
79 modelI.transform.rotate(0,1,0,delta*30f); // Continua a rotación, non empeza de 0
80 }
81
82 instanceNave.transform.setTranslation(pos,0,0);
83
84
85 camara3d.lookAt(pos,0,0);
86 camara3d.update();
87
88 modelBatch.begin(camara3d);
89
90 modelBatch.render(instances,environment);
91 modelBatch.render(instanceNave,environment);
92
93 modelBatch.end();
94 }
95
96
97 @Override
98 public void resize(int width, int height) {
99 // TODO Auto-generated method stub
100
101 camara3d.setFieldOfView(67);
102 camara3d.viewportWidth=width;
103 camara3d.viewportHeight=height;
104
105 Gdx.input.setInputProcessor(camController);
106
107 camara3d.position.set(0f,0f,15f);
108 camara3d.lookAt(0,0,0);
109 camara3d.near=1;
110 camara3d.far=300f;
111 camara3d.update();
112
113
114 }
115
116 @Override
117 public void show() {
118 // TODO Auto-generated method stub
119 }
120
121 @Override
122 public void hide() {
123 // TODO Auto-generated method stub
124
125 }
126
127 @Override
128 public void pause() {
129 // TODO Auto-generated method stub
130
131 }
132
133 @Override
134 public void resume() {
135 // TODO Auto-generated method stub
136
137 }
138
139 @Override
140 public void dispose() {
141 // TODO Auto-generated method stub
142
143 modelBatch.dispose();
144 instances.clear();
145
146
147 }
148
149 }
Comentemos o código:
- Liñas 51-52: Creamos un ModelInstance a partires do Model da nave e rotamos no eixe Y 90 grados a nave, para poñela cara os cubos.
- Liñas 54-62: Creamos o array de ModelInstance. Os trasladamos chamando ó método setTranslate e dous deles (os números 4 e 5) ademais de trasladalos os escalamos á metade do seu tamaño orixinal.
- Liñas 78-80: Rotamos todos os cubos. Como xa están trasladados, a rotación é no punto onde se atopan (como o movemento de rotación da Terra). Chamamos a translate e non a setToTranslate xa que este último inicializa a matriz e polo tanto movería todos os cubos á posición (0,0,0).
- Liña 82: Movemos a nave.
- Liñas 85-86: Facemos que a cámara siga á nave.
- Liñas 90-91: Renderizamos os ModelInstace dos bloques (array) e a nave.
Ó final teremos como resultado isto:
Modelo-Vista-Controlador: Movendo os Modelos 3D
Imos modificar o exercicio anterior para que siga a arquitectura Modelo-Vista.
O controlador xa o sabedes implementar das unidades referidas o xogo 2D polo que por motivos de 'rapidez' non o imos utilizar neste exemplo.
Preparación:
- Crear unha nova clase de nome EX_4_MoverModelosMVC.
- Cambiade a clase PracticasNovaApi3D para que chame á nova clase.
Empecemos creando os modelos:
Código da clase Elemento3D
Obxectivo: Crear o Modelo. Saltamos o paso de crear a clase Mundo para facelo máis sinxelo.
1 import com.badlogic.gdx.math.Matrix4;
2 import com.badlogic.gdx.math.Vector3;
3
4 public class Elemento3D {
5 public Matrix4 matriz;
6 public Vector3 posicion;
7 public float escala;
8 public Vector3 velocidade;
9 public Vector3 rotacion;
10
11 public float anguloRotacion;
12 private int velocidadeRotacion;
13
14 private Vector3 temp;
15
16 public Elemento3D(Vector3 pos, float escala,Vector3 velocidade, Vector3 rotacion,int velocidadeRotacion){
17 matriz = new Matrix4();
18 posicion = pos;
19 this.escala=escala;
20 this.velocidade = velocidade;
21 this.rotacion=rotacion;
22 anguloRotacion=0;
23 this.velocidadeRotacion=velocidadeRotacion;
24
25 temp = new Vector3();
26 }
27
28
29 public void update(float delta){
30
31 temp.set(velocidade);
32 posicion.add(temp.scl(delta));
33 anguloRotacion+=delta*velocidadeRotacion;
34
35 matriz.idt();
36 matriz.translate(posicion);
37 matriz.scl(escala);
38 matriz.rotate(rotacion, anguloRotacion);
39
40 }
41
42 }
Como vemos o máis importante a ter en conta é que imos gardar nesta clase a Matriz de modelado que vai usarse para representar a figura.
Fixarse como sempre partimos da matriz de identidade e aplicamos a traslación, escala e rotación (liñas 35-38).
- Liñas 24-25: Agora xa non necesitamos un array de ModelInstances. Imos gardar un único ModelInstance por cada tipo de elemento gráfico (unha nave e un bloque).
- Liñas 27-28: Gardamos en obxectos da clase Elemento3D os datos de cada figura.
- Liñas 44-53: Facemos o mesmo que no exercicio anterior (os mesmos datos) pero esta vez son gardados en obxectos que pertencen á clase Elementos3D.
- Liña 76: A cámara mira cara a posición da nave.
- Liñas 81-86: Moi importante: Actualizamos a MATRIZ de cada bloque e asinamos ó ModelInstance a matriz gardada.
- Liñas 88-92: Movemos a nave e a rotamos para que estea cara os bloques. Actualizamos a matriz e asinamos ó ModelInstance da nave a matriz gardada.
- Liña 145: Limpamos o array del bloques.
Código da clase EX_4_MoverModelosMVC
Obxectivo: Facemos o mesmo exercicio anterior pero utilizando a matriz gardada na clase Elemento3D.
1 import com.badlogic.gdx.Gdx;
2 import com.badlogic.gdx.Screen;
3 import com.badlogic.gdx.assets.AssetManager;
4 import com.badlogic.gdx.graphics.GL20;
5 import com.badlogic.gdx.graphics.PerspectiveCamera;
6 import com.badlogic.gdx.graphics.g3d.Environment;
7 import com.badlogic.gdx.graphics.g3d.Model;
8 import com.badlogic.gdx.graphics.g3d.ModelBatch;
9 import com.badlogic.gdx.graphics.g3d.ModelInstance;
10 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
11 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
12 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
13 import com.badlogic.gdx.math.Vector3;
14 import com.badlogic.gdx.utils.Array;
15 import com.plategaxogo3davanzado.angel.Elemento3D;
16
17
18
19 public class EX_4_MoverModelosMVC implements Screen {
20
21 private PerspectiveCamera camara3d;
22 private ModelBatch modelBatch;
23
24 private ModelInstance instanceNave;
25 private ModelInstance instanceBloques;
26
27 private Array<Elemento3D>bloques;
28 private Elemento3D nave;
29
30 private Environment environment;
31 private CameraInputController camController;
32
33
34 public EX_4_MoverModelosMVC(){
35 camara3d = new PerspectiveCamera();
36 camController = new CameraInputController(camara3d);
37 Gdx.input.setInputProcessor(camController);
38
39 modelBatch = new ModelBatch();
40 environment = new Environment();
41 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
42 environment.add(new DirectionalLight().set(1f, 1f, 1f, 1f, 0f, 0f));
43
44 bloques = new Array<Elemento3D>();
45 for (int cont=1; cont < 10;cont++){
46 if (cont>3 && cont<6){
47 bloques.add(new Elemento3D(new Vector3(3*cont, 0, 0),0.5f,new Vector3(0,0,0),new Vector3(1,1,1),30));
48 }
49 else
50 bloques.add(new Elemento3D(new Vector3(3*cont, 0, 0),1f,new Vector3(0,0,0),new Vector3(1,1,1),50));
51 }
52
53 nave = new Elemento3D(new Vector3(0,0,0), 1, new Vector3(1f, 0, 0),new Vector3(0,0,0),0);
54
55
56
57 AssetManager assets = new AssetManager();
58 assets.load("ship.obj", Model.class);
59 assets.load("cube.g3db", Model.class);
60 assets.finishLoading();
61
62 Model modelNave = assets.get("ship.obj", Model.class);
63 Model modelCubo = assets.get("cube.g3db", Model.class);
64
65 instanceBloques = new ModelInstance(modelCubo);
66 instanceNave = new ModelInstance(modelNave);
67 }
68
69 @Override
70 public void render(float delta) {
71 // TODO Auto-generated method stub
72 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
73
74 camController.update();
75
76 camara3d.lookAt(nave.posicion);
77 camara3d.update();
78
79 modelBatch.begin(camara3d);
80
81 for (Elemento3D bloque : bloques){
82 bloque.update(delta);
83 instanceBloques.transform.set(bloque.matriz);
84 modelBatch.render(instanceBloques,environment);
85
86 }
87
88 nave.posicion.x += nave.velocidade.x*delta;
89 nave.update(delta);
90 nave.matriz.rotate(0, 1,0,90);
91 instanceNave.transform.set(nave.matriz);
92 modelBatch.render(instanceNave,environment);
93
94 modelBatch.end();
95 }
96
97
98 @Override
99 public void resize(int width, int height) {
100 // TODO Auto-generated method stub
101
102 camara3d.fieldOfView=67;
103 camara3d.viewportWidth=width;
104 camara3d.viewportHeight=height;
105
106 Gdx.input.setInputProcessor(camController);
107
108 camara3d.position.set(0f,0f,15f);
109 camara3d.lookAt(0,0,0);
110 camara3d.near=1;
111 camara3d.far=300f;
112 camara3d.update();
113
114
115 }
116
117 @Override
118 public void show() {
119 // TODO Auto-generated method stub
120 }
121
122 @Override
123 public void hide() {
124 // TODO Auto-generated method stub
125
126 }
127
128 @Override
129 public void pause() {
130 // TODO Auto-generated method stub
131
132 }
133
134 @Override
135 public void resume() {
136 // TODO Auto-generated method stub
137
138 }
139
140 @Override
141 public void dispose() {
142 // TODO Auto-generated method stub
143
144 modelBatch.dispose();
145 bloques.clear();
146
147
148 }
149
150 }
Vexamos as diferenzas:
- Liñas 24-25: Agora xa non necesitamos un array de ModelInstances. Imos gardar un único ModelInstance por cada tipo de elemento gráfico (unha nave e un bloque).
- Liñas 27-28: Gardamos en obxectos da clase Elemento3D os datos de cada figura.
- Liñas 44-53: Facemos o mesmo que no exercicio anterior (os mesmos datos) pero esta vez son gardados en obxectos que pertencen á clase Elementos3D.
- Liña 76: A cámara mira cara a posición da nave.
- Liñas 81-86: Moi importante: Actualizamos a MATRIZ de cada bloque e asinamos ó ModelInstance a matriz gardada.
- Liñas 88-92: Movemos a nave e a rotamos para que estea cara os bloques. Actualizamos a matriz e asinamos ó ModelInstance da nave a matriz gardada.
- Liña 145: Limpamos o array del bloques.
Se executades o código o resultado é o mesmo que no caso anterior.
Por eficiencia pode ser mellor ter un array de ModelInstances e renderizalos todos de vez. Isto o podemos facer igual pero teríamos que asinar a cada elemento do array de ModelInstace a matriz gardada no array de Elementos3D.
No blog de Xoppa tedes unha entrada no que amosa como podemos cargar todos os modelos de vez dun só acceso á disco: http://blog.xoppa.com/loading-a-scene-with-libgdx/
-- Ángel D. Fernández González -- (2015).