JavaRTS/core/polygon3D.java
2019-05-14 00:49:24 +12:00

545 lines
15 KiB
Java

package core;
import entity.solidObject;
public class polygon3D {
//The vertex of the polygon with the respect of the world/camera coordinate
public vector[] vertex3D, tempVertex;
//The vertex of the polygon after clipping
public vector[] vertex2D;
//the normal of the polygon with the respect of the world/camera coordinate
public vector normal;
//the centre of the polygon with the respect of the world/camera coordinate
public vector centre;
//The number of vertex
public byte L;
//whether the polygon is completely bounded by the screen
public boolean withinViewScreen;
//These 3 vectors map the 3 corners of the texture to the world coordinate
public vector origin, rightEnd, bottomEnd;
//texture that is bind to the polygon
public texture myTexture;
//only need to shade if the current fragment's depth in light space is far enough from the lightmap value
//the threshold value is related to the orientation of the polygon in light space
//if the normal of the polygon is almost perpendicular to the light direction, then we need a bigger threshold
//to avoid shadow acne
public int shadowBias;
//Information about the texture
public int heightMask, widthMask, widthBits, heightBits;
public float textureWidth, textureHeight;
public float textureWidthInverse, textureHeightInverse;
//The size of one texel
public float textureScaleX, textureScaleY;
//the number of times texture repeats itself along the polygon
public float scaleX, scaleY;
//the 3D object which this polygon belongs to
public solidObject parentObject;
//A pool of vectors which will be used for vector arithmetic
public static vector
tempVector1 = new vector(0,0,0),
tempVector2 = new vector(0,0,0),
tempVector3 = new vector(0,0,0),
tempVector4 = new vector(0,0,0),
tempVector5 = new vector(0,0,0),
tempVector6 = new vector(0,0,0);
//whether the polygon is visible
public boolean visible;
//whether the polygon is visible in light space
public boolean visibleInLightSpace;
//number of vertices are behind of the clip plane
public int numberOfVerticesBehindClipPlane;
//the amount of vertex after clipping
public int visibleCount;
//type of the polygon
public byte type;
//the diffuse/ambient intensity of this polygon
public int diffuse_I;
public int Ambient_I = 16; //the default ambient intensity is 16
public int reflectance = 96;
//diffuse value at vertex (only for polygons with 3 vertex)
public byte[] diffuse = new byte[3];
//default light source
public vector lightDirection = sunLight.lightDirection;
//the color of polygon if it is defined as soild
public int color;
//light map texture for this polygon
//public int lightMapTextureIndex;
//max texel change rate in x direction;
//public int max_dx = 512;
//Whether origin, rightEnd and bottomEnd vectors match exactly the corners of the polygon
public boolean textureFitPolygon;
public boolean smoothShading;
public int textureScaledWidth;
public double I_left, I_right, I_difference;
public static boolean recreateTextureCoordinateFlag;
//Constuctor of the polygon class, it will only accept convex polygons
public polygon3D(vector[] vertex3D, vector origin, vector rightEnd, vector bottomEnd, texture myTexture, float scaleX, float scaleY, int type){
this.type = (byte)type;
this.vertex3D = vertex3D;
this.myTexture = myTexture;
this.scaleX = scaleX;
this.scaleY = scaleY;
L = (byte)vertex3D.length;
diffuse_I = 31;
shadowBias = 30000;
if(recreateTextureCoordinateFlag){
origin = origin.myClone();
rightEnd = rightEnd.myClone();
bottomEnd = bottomEnd.myClone();
}
//test if origin, rightEnd and bottomEnd vectors match exactly the corners of the polygon
for(int i = 0; i < L; i++){
if(vertex3D[i] == origin){
textureFitPolygon = true;
break;
}
textureFitPolygon = false;
}
if(textureFitPolygon){
for(int i = 0; i < L; i++){
if(vertex3D[i] == rightEnd){
textureFitPolygon = true;
break;
}
textureFitPolygon = false;
}
}
if(textureFitPolygon){
for(int i = 0; i < L; i++){
if(vertex3D[i] == bottomEnd){
textureFitPolygon = true;
break;
}
textureFitPolygon = false;
}
}
//set the tempVertex to the vertex3D
tempVertex = new vector[L];
for(int i = 0; i < L; i++){
tempVertex[i] = new vector(0,0,0);
tempVertex[i].set(vertex3D[i]);
}
//find normal vector of the polygon (in world coordinate)
normal = new vector(0,0,0);
findNormal();
//find centre of the polygon (in world coordinate)
centre = new vector(0,0,0);
for(int i = 0; i < tempVertex.length; i++)
centre.add(tempVertex[i]);
centre.scale(1.0f/tempVertex.length);
if(origin != null){
this.origin = origin;
this.rightEnd = rightEnd;
this.bottomEnd = bottomEnd;
}
//get the texture information if the polygon is bonded with a texture
if(myTexture != null){
textureWidth = scaleX* myTexture.width;
textureHeight =scaleY*myTexture.height;
textureWidthInverse = 1f/textureWidth;
textureHeightInverse = 1f/textureHeight;
heightMask = myTexture.heightMask;
widthMask = myTexture.widthMask;
widthBits = myTexture.widthBits;
heightBits = myTexture.heightBits;
textureScaledWidth = (int)(myTexture.width*scaleX);
//find the size of one texel in the world coordinate
tempVector1.set(origin);
tempVector1.subtract(rightEnd);
float l = tempVector1.getLength();
textureScaleX = l/myTexture.width;
tempVector1.set(origin);
tempVector1.subtract(bottomEnd);
l = tempVector1.getLength();
textureScaleY = l/myTexture.height;
textureScaleX = textureScaleX/scaleX;
textureScaleY = textureScaleY/scaleY;
}else{
textureScaleX = 1;
textureScaleY = 1;
}
//init vertex2D, notice that the size of vertex2D is bigger than vertex3D, because after clipping
//it is possilbe to generate one more vertex for the polygon.
vertex2D = new vector[L+1];
for(int i = 0; i < L+1; i++)
vertex2D[i] = new vector(0,0,0);
//find the initial diffuse intensity of this polygon
findDiffuse();
}
//update this polygon based on camera movement in each frame
public void update(){
//back face culling
tempVector1.set(camera.position);
tempVector1.subtract(vertex3D[0]);
if(tempVector1.dot(normal) <= 0){
visible = false;
return;
}
//translate vertex from world space to camera space
float x = 0,y = 0, z = 0,
camX = camera.position.x, camY = camera.position.y, camZ = camera.position.z,
sinXZ = camera.sinXZ_angle,
cosXZ = camera.cosXZ_angle,
sinYZ = camera.sinYZ_angle,
cosYZ = camera.cosYZ_angle;
withinViewScreen = false;
visible = true;
if(parentObject != null){
if(parentObject.withinViewScreen){
for(int i = 0; i < L; i++){
tempVector5 = vertex3D[i];
tempVector6 = vertex2D[i];
//shifting
x = tempVector5.x - camX;
y = tempVector5.y - camY;
z = tempVector5.z - camZ;
//rotating
tempVector6.x = cosXZ*x - sinXZ*z;
tempVector6.z = sinXZ*x + cosXZ*z;
z = tempVector6.z;
tempVector6.y = cosYZ*y - sinYZ*z;
tempVector6.z = sinYZ*y + cosYZ*z;
tempVector6.updateLocation();
}
withinViewScreen = true;
visible = true;
visibleCount = L;
return;
}
}
for(int i = 0; i < L; i++){
//shifting
x = vertex3D[i].x - camX;
y = vertex3D[i].y - camY;
z = vertex3D[i].z - camZ;
//rotating
tempVertex[i].x = cosXZ*x - sinXZ*z;
tempVertex[i].z = sinXZ*x + cosXZ*z;
z = tempVertex[i].z;
tempVertex[i].y = cosYZ*y - sinYZ*z;
tempVertex[i].z = sinYZ*y + cosYZ*z;
}
//find the number of vertices that are behind clip plane
numberOfVerticesBehindClipPlane = 0;
for(int i = 0; i < L; i++){
if(tempVertex[i].z <= 0.1){
numberOfVerticesBehindClipPlane++;
}
}
//if all vertices
if(numberOfVerticesBehindClipPlane == L){
visible = false;
return;
}
findClipping();
}
//update vision polygon
public void update_visionPolygon(){
vector cameraPosition = postProcessingThread.cameraPosition;
//translate vertex from world space to camera space
float x = 0,y = 0, z = 0,
camX = cameraPosition.x, camY = cameraPosition.y, camZ = cameraPosition.z,
sinXZ = postProcessingThread.sinXZ,
cosXZ = postProcessingThread.cosXZ,
sinYZ = postProcessingThread.sinYZ,
cosYZ = postProcessingThread.cosYZ;
for(int i = 0; i < L; i++){
//shifting
x = vertex3D[i].x - camX;
y = vertex3D[i].y - camY;
z = vertex3D[i].z - camZ;
//rotating
vertex2D[i].x = cosXZ*x - sinXZ*z;
vertex2D[i].z = sinXZ*x + cosXZ*z;
z = vertex2D[i].z;
vertex2D[i].y = cosYZ*y - sinYZ*z;
vertex2D[i].z = sinYZ*y + cosYZ*z;
if(vertex2D[i].z < 0.01)
vertex2D[i].z = 0.01f;
vertex2D[i].updateLocation();
}
visible = true;
visibleCount = L;
return;
}
//update the polygon in light space
public void update_lightspace(){
//back face culling
visibleInLightSpace = true;
float normalDotLight = normal.dot(sunLight.lightDirection);
if(normalDotLight >= 0){
visibleInLightSpace = false;
return;
}
//translate vertex from world space to light space
float x = 0,y = 0, z = 0,
sunX = sunLight.position.x, sunY = sunLight.position.y, sunZ = sunLight.position.z,
sinXZ = sunLight.sinXZ_angle,
cosXZ = sunLight.cosXZ_angle,
sinYZ = sunLight.sinYZ_angle,
cosYZ = sunLight.cosYZ_angle;
for(int i = 0; i < L; i++){
tempVector5 = vertex3D[i];
tempVector6 = vertex2D[i];
//shifting
x = tempVector5.x - sunX;
y = tempVector5.y - sunY;
z = tempVector5.z - sunZ;
//rotating
tempVector6.x = cosXZ*x - sinXZ*z;
tempVector6.z = sinXZ*x + cosXZ*z;
z = tempVector6.z;
tempVector6.y = cosYZ*y - sinYZ*z;
tempVector6.z = sinYZ*y + cosYZ*z;
tempVector6.updateLocationOrthognal();
tempVector6.z_lightspace = tempVector6.z;
tempVector5.z_lightspace = tempVector6.z;
tempVector5.screenX_lightspace = tempVector6.screenX_lightspace;
tempVector5.screenY_lightspace = tempVector6.screenY_lightspace;
}
if(type == 1){
rasterizer.renderShadow(this);
}else{
if(type == 4)
rasterizer.renderShadowRemover(this);
else if(type == 9)
rasterizer.renderCloakedShadow(this);
}
}
public void update_lightspace_withoutDrawing(){
//back face culling
visibleInLightSpace = true;
if(sunLight.lightDirection.dot(normal) > 0){
visibleInLightSpace = false;
return;
}
//translate vertex from world space to light space
float x = 0,y = 0, z = 0,
sunX = sunLight.position.x, sunY = sunLight.position.y, sunZ = sunLight.position.z,
sinXZ = sunLight.sinXZ_angle,
cosXZ = sunLight.cosXZ_angle,
sinYZ = sunLight.sinYZ_angle,
cosYZ = sunLight.cosYZ_angle;
for(int i = 0; i < L; i++){
//shifting
x = vertex3D[i].x - sunX;
y = vertex3D[i].y - sunY;
z = vertex3D[i].z - sunZ;
//rotating
vertex2D[i].x = cosXZ*x - sinXZ*z;
vertex2D[i].z = sinXZ*x + cosXZ*z;
z = vertex2D[i].z;
vertex2D[i].y = cosYZ*y - sinYZ*z;
vertex2D[i].z = sinYZ*y + cosYZ*z;
vertex2D[i].updateLocationOrthognal();
vertex2D[i].z_lightspace = vertex2D[i].z;
vertex3D[i].z_lightspace = vertex2D[i].z;
vertex3D[i].screenX_lightspace = vertex2D[i].screenX_lightspace;
vertex3D[i].screenY_lightspace = vertex2D[i].screenY_lightspace;
}
}
//clipping
public void findClipping(){
visibleCount = 0;
//the clipping algorithm iterate through all the vertex of the polygons, if it finds
//a vertex which is behind the clipping plane(z = 0.001), then generate 2 new vertex on the
//clipping plane
for(int i = 0; i < L; i++){
if(tempVertex[i].z >= 0.1){
vertex2D[visibleCount].set(tempVertex[i]);
vertex2D[visibleCount].updateLocation();
visibleCount++;
} else{
int index = (i+L - 1)%L;
if(tempVertex[index].z >= 0.1005){
approximatePoint(visibleCount, tempVertex[i], tempVertex[index]);
visibleCount++;
}
index = (i+1)%L;
if(tempVertex[index].z >= 0.1005){
approximatePoint(visibleCount, tempVertex[i], tempVertex[index]);
visibleCount++;
}
}
}
}
//find the approximate projection point on the clipping plane
public void approximatePoint(int index, vector behindPoint, vector frontPoint){
tempVector1.set(frontPoint.x - behindPoint.x, frontPoint.y - behindPoint.y, frontPoint.z - behindPoint.z);
tempVector1.scale((frontPoint.z- 0.1f)/tempVector1.z);
vertex2D[index].set(frontPoint.x, frontPoint.y, frontPoint.z);
vertex2D[index].subtract(tempVector1);
vertex2D[index].updateLocation();
}
//find diffuse intensity of this polygon
public void findDiffuse(){
//calculate the diffuse intensity from the light source
tempVector1.set(-lightDirection.x, -lightDirection.y, -lightDirection.z);
double I = normal.dot(tempVector1);
diffuse_I = Ambient_I + (int)(I*reflectance);
if(I < 0)
diffuse_I = Ambient_I;
}
//create a smooth 1 dimensional shade map for the polygon. Only works if the polygon belongs to a
//cylindrical object.
public void createShadeSpan(vector theCenter, vector v0, vector v1){
smoothShading = true;
tempVector1.set(v0);
tempVector1.subtract(theCenter);
tempVector1.unit();
tempVector2.set(v1);
tempVector2.subtract(theCenter);
tempVector2.unit();
tempVector3.set(-lightDirection.x, -lightDirection.y, -lightDirection.z);
I_left = tempVector1.dot(tempVector3)*reflectance + Ambient_I;
if(I_left < Ambient_I)
I_left = Ambient_I;
I_right = tempVector2.dot(tempVector3)*reflectance + Ambient_I;
if(I_right < Ambient_I)
I_right = Ambient_I;
I_difference = (I_right - I_left)/textureScaledWidth;
}
public void findNormal(){
tempVector1.set(vertex3D[1]);
tempVector1.subtract(vertex3D[0]);
tempVector2.set(vertex3D[2]);
tempVector2.subtract(vertex3D[1]);
normal.cross(tempVector1, tempVector2);
normal.unit();
}
public void draw(){
//send this polygon to rasterizer
if(visible){
mainThread.theAssetManager.polygonCount++;
rasterizer.rasterize(this);
}
}
}