package java3d;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL10;
import framework.model3D.Position3D;
import android.opengl.GLU;
import android.opengl.GLUtils;
public class GraphicsContext3D {
private GL10 gl;
private Appearance appearance = null;
private boolean bFixAspect = false;
private float aspect;
private HashMap<Texture, Integer> textureRegistry = new HashMap<Texture, Integer>();
private HashMap<Light, Integer> lightRegistry = new HashMap<Light, Integer>();
public GraphicsContext3D(GL10 gl) {
init(gl);
}
public GraphicsContext3D setGL10(GL10 gl) {
if (this.gl != gl) {
init(gl);
}
return this;
}
public void fixAspect(float aspect) {
this.bFixAspect = true;
this.aspect = aspect;
}
public void init(GL10 gl) {
this.gl = gl;
// デプスバッファのテスト機能を有効にする
gl.glEnable(GL10.GL_DEPTH_TEST);
// 陰面消去の動作を設定
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glDepthMask(true);
// ライトを有効にする
gl.glEnable(GL10.GL_LIGHTING);
// どの光源を使用するか指定
gl.glEnable(GL10.GL_LIGHT0);
gl.glClearColor(0.0f,0.0f,0.0f,0.0f);
gl.glClearDepthf(1.0f);
}
public void update(int width, int height, float fovx, float zNear, float zFar,boolean fParallel) {
setGL10(gl);
if (!bFixAspect) {
aspect = (float)width / (float)height;
}
// ビューポートの設定
gl.glViewport(0, 0, width, height);
// カメラの設定
gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換
gl.glLoadIdentity(); // 座標の初期化
// 画角の設定
float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f);
if(!fParallel){
GLU.gluPerspective(gl,
fovy, //Y方向の画角
aspect, //アスペクト比
zNear, //ニアクリップ
zFar);//ファークリップ
}else{
float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0));
float bottom = -top;
float left = bottom * aspect;
float right = top * aspect;
gl.glOrthof(left, right, bottom, top, zNear, zFar);
}
}
public void update(float fovx, float zNear, float zFar, Position3D eye, Position3D center, Vector3d up, boolean fParallel) {
// 表示画面とデプスバッファのクリア
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// カメラの設定
gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換
gl.glLoadIdentity(); // 座標の初期化
// 画角の設定
float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f);
if (!fParallel) {
GLU.gluPerspective(gl,
fovy, //Y方向の画角
aspect, //アスペクト比
zNear, //ニアクリップ
zFar);//ファークリップ
} else {
float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0));
float bottom = -top;
float left = bottom * aspect;
float right = top * aspect;
gl.glOrthof(left, right, bottom, top, zNear, zFar);
}
// モデルビュー行列の指定
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 座標の初期化
gl.glLoadIdentity();
// カメラ外部パラメータの設定
GLU.gluLookAt(gl,
(float)eye.getX(), (float)eye.getY(), (float)eye.getZ(),
(float)center.getX(), (float)center.getY(), (float)center.getZ(),
(float)up.getX(), (float)up.getY(), (float)up.getZ());
}
public void setLight(Light l, int i) {
lightRegistry.put(l, i);
Color3f c = l.getColor();
float color[] = {c.r, c.g, c.b, 1.0f};
gl.glEnable(GL10.GL_LIGHT0 + i);
if (l instanceof AmbientLight) {
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, color, 0);
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0);
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0);
} else {
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0);
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, color, 0);
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{1.0f, 1.0f, 1.0f, 1.0f}, 0);
}
}
public void updateLightState(Light l) {
Integer i = lightRegistry.get(l);
if (i == null) {
// Object3D内部に配置されている光源に対しては、初期化時にsetLight()が呼ばれないため
i = lightRegistry.size();
setLight(l, i);
}
if (l instanceof DirectionalLight) {
Vector3f v = ((DirectionalLight)l).getDirection();
float direction[] = {-v.x, -v.y, -v.z, 0.0f};
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, direction, 0);
} else if (l instanceof PointLight) {
Point3f p = ((PointLight)l).getPosition();
float position[] = {p.x, p.y, p.z, 1.0f};
gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, position, 0);
}
}
public void pushMatrix() {
gl.glPushMatrix();
}
public void popMatrix() {
gl.glPopMatrix();
}
public void multMatrix(Transform3D transform) {
float m[] = transform.getMatrix();
gl.glMultMatrixf(m, 0);
}
public void setAppearance(Appearance appearance) {
this.appearance = appearance;
Material material = appearance.getMaterial();
if (material != null) {
// 表面属性の設定
gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_DIFFUSE, material.diffuse, 0);
gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_AMBIENT, material.ambient, 0);
gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_SPECULAR, material.specular, 0);
gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_EMISSION, material.emissive, 0);
gl.glMaterialf(GL10.GL_FRONT, GL10.GL_SHININESS, material.shininess);
}
if (appearance.getTextureUnitCount() == 0) {
// テクスチャユニットを使っていない場合(通常のテクスチャの場合)
Texture tex = appearance.getTexture();
if (tex != null) registerTexture(tex);
} else {
// テクスチャユニットを使っている場合
for (int n = 0; n < appearance.getTextureUnitCount(); n++) {
TextureUnitState tus = appearance.getTextureUnitState(n);
Texture tex = tus.getTexture();
if (tex != null) {
gl.glActiveTexture(GL10.GL_TEXTURE1 + n);
registerTexture(tex);
}
}
}
}
private void registerTexture(Texture tex) {
if (textureRegistry.get(tex) == null) {
// テクスチャの登録
int[] textureId = new int[1];
gl.glGenTextures(1, textureId, 0);
if (tex instanceof TextureCubeMap) {
// 立方体マップの場合(GL10では対応していない)
} else {
// 通常の場合
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId[0]);
ImageComponent[] imageComponents = tex.getImages();
for (int level = 0; level < imageComponents.length; level++) {
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, ((ImageComponent2D)imageComponents[level]).getBitmap(), 0);
// ((ImageComponent2D)imageComponents[i]).getBitmap().recycle();
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
}
}
textureRegistry.put(tex, textureId[0]);
}
}
public void draw(Shape3D node) {
if (node == null) return;
setAppearance(node.getAppearance());
draw(node.getGeometry());
}
public void draw(Geometry g) {
if (g == null) return;
if (appearance.getTextureUnitCount() == 0) {
// テクスチャユニットを使っていない場合(通常のテクスチャの場合)
Texture tex = appearance.getTexture();
if (tex != null) {
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提
TextureAttributes ta = appearance.getTextureAttributes();
if (ta != null) setTextureAttributes(ta);
}
} else {
// テクスチャユニットを使っている場合
for (int n = 0; n < appearance.getTextureUnitCount(); n++) {
TextureUnitState tus = appearance.getTextureUnitState(n);
Texture tex = tus.getTexture();
if (tex != null) {
gl.glActiveTexture(GL10.GL_TEXTURE0 + n);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提
TextureAttributes ta = tus.getTextureAttributes();
if (ta != null) setTextureAttributes(ta);
}
}
}
if (g instanceof GeometryArray) {
// バッファの設定
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
FloatBuffer vertexBuffer = ((GeometryArray)g).getVertexBuffer();
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) {
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
FloatBuffer normalBuffer = ((GeometryArray)g).getNormalBuffer();
gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer);
}
if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer();
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, uvBuffer);
} else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer();
gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, uvBuffer);
} else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer();
gl.glTexCoordPointer(4, GL10.GL_FLOAT, 0, uvBuffer);
}
// ジオメトリの描画
if (g instanceof TriangleArray) {
TriangleArray ta = (TriangleArray)g;
int vertexCount = ta.getVertexCount();
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexCount);
} else if (g instanceof TriangleFanArray) {
TriangleFanArray ta = (TriangleFanArray)g;
int start = 0;
int[] stripVertexCounts = new int[ta.getNumStrips()];
ta.getStripVertexCounts(stripVertexCounts);
for (int n = 0; n < ta.getNumStrips(); n++) {
int vertexCount = stripVertexCounts[n];
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, start, vertexCount);
start += vertexCount;
}
} else if (g instanceof TriangleStripArray) {
TriangleStripArray ta = (TriangleStripArray)g;
int start = 0;
int[] stripVertexCounts = new int[ta.getNumStrips()];
ta.getStripVertexCounts(stripVertexCounts);
for (int n = 0; n < ta.getNumStrips(); n++) {
int vertexCount = stripVertexCounts[n];
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, start, vertexCount);
start += vertexCount;
}
} else if (g instanceof IndexedTriangleArray) {
IndexedTriangleArray ita = (IndexedTriangleArray)g;
int vertexCount = ((IndexedTriangleArray)g).getIndexCount();
gl.glDrawElements(GL10.GL_TRIANGLES, vertexCount, GL10.GL_UNSIGNED_SHORT, ita.getCoordinateIndexBuffer());
} else if (g instanceof IndexedTriangleFanArray) {
IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g;
ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer();
int start = 0;
int[] stripIndexCounts = new int[igsa.getNumStrips()];
igsa.getStripIndexCounts(stripIndexCounts);
for (int n = 0; n < igsa.getNumStrips(); n++) {
int vertexCount = stripIndexCounts[n];
indexBuffer.position(start);
gl.glDrawElements(GL10.GL_TRIANGLE_FAN, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer);
start += vertexCount;
}
} else if (g instanceof IndexedTriangleStripArray) {
IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g;
ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer();
int start = 0;
int[] stripIndexCounts = new int[igsa.getNumStrips()];
igsa.getStripIndexCounts(stripIndexCounts);
for (int n = 0; n < igsa.getNumStrips(); n++) {
int vertexCount = stripIndexCounts[n];
indexBuffer.position(start);
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer);
start += vertexCount;
}
}
if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
} else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
} else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) {
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
if (appearance.getTextureUnitCount() == 0) {
// テクスチャユニットを使っていない場合(通常のテクスチャの場合)
if (appearance.getTexture() != null) {
gl.glDisable(GL10.GL_TEXTURE_2D);
}
} else {
// テクスチャユニットを使っている場合
for (int n = 0; n < appearance.getTextureUnitCount(); n++) {
gl.glActiveTexture(GL10.GL_TEXTURE0 + n);
gl.glDisable(GL10.GL_TEXTURE_2D);
}
gl.glActiveTexture(GL10.GL_TEXTURE0);
}
}
private void setTextureAttributes(TextureAttributes ta) {
int textureMode = ta.getTextureMode();
switch (textureMode) {
case TextureAttributes.REPLACE:
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
break;
case TextureAttributes.BLEND:
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_BLEND);
break;
// case TextureAttributes.COMBINE:
// gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_COMBINE);
// break;
case TextureAttributes.MODULATE:
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
break;
case TextureAttributes.DECAL:
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL);
break;
}
// int perspCorrectionMode = ta.getPerspectiveCorrectionMode();
// switch (perspCorrectionMode) {
// case TextureAttributes.NICEST:
// break;
// case TextureAttributes.FASTEST:
// break;
// }
}
}