All Downloads are FREE. Search and download functionalities are using the official Maven repository.

games.rednblack.editor.renderer.box2dLight.PositionalLight Maven / Gradle / Ivy

The newest version!

package games.rednblack.editor.renderer.box2dLight;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.Mesh.VertexDataType;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;

/**
 * Abstract base class for all positional lights
 * 
 * 

Extends {@link Light} * * @author kalle_h */ public abstract class PositionalLight extends Light { Color tmpColor = new Color(); protected final Vector2 tmpEnd = new Vector2(); protected final Vector2 start = new Vector2(); protected Body body; protected float bodyOffsetX; protected float bodyOffsetY; protected float bodyAngleOffset; protected float sin[]; protected float cos[]; protected float endX[]; protected float endY[]; /** * Creates new positional light and automatically adds it to the specified * {@link RayHandler} instance. * * @param rayHandler * not null instance of RayHandler * @param rays * number of rays - more rays make light to look more realistic * but will decrease performance, can't be less than MIN_RAYS * @param color * light color * @param distance * light distance (if applicable) * @param x * horizontal position in world coordinates * @param y * vertical position in world coordinates * @param directionDegree * direction in degrees (if applicable) */ public PositionalLight(RayHandler rayHandler, int rays, Color color, float distance, float x, float y, float directionDegree) { super(rayHandler, rays, color, distance, directionDegree); start.x = x; start.y = y; VertexDataType vertexDataType = VertexDataType.VertexArray; if (Gdx.gl30 != null) { vertexDataType = VertexDataType.VertexBufferObjectWithVAO; } lightMesh = new Mesh(vertexDataType, false, vertexNum, 0, new VertexAttribute(Usage.Position, 2, "vertex_positions"), new VertexAttribute(Usage.ColorPacked, 4, "quad_colors"), new VertexAttribute(Usage.Generic, 1, "s")); softShadowMesh = new Mesh(vertexDataType, false, vertexNum * 2, 0, new VertexAttribute(Usage.Position, 2, "vertex_positions"), new VertexAttribute(Usage.ColorPacked, 4, "quad_colors"), new VertexAttribute(Usage.Generic, 1, "s")); setMesh(); } @Override void update() { updateBody(); if (cull()) return; if (staticLight && !dirty) return; dirty = false; updateMesh(); } @Override void render() { if (rayHandler.culling && culled) return; rayHandler.lightRenderedLastFrame++; lightMesh.render(rayHandler.lightShader, GL20.GL_TRIANGLE_FAN, 0, vertexNum); if (soft && !xray && !rayHandler.pseudo3d) { softShadowMesh.render( rayHandler.lightShader, GL20.GL_TRIANGLE_STRIP, 0, (vertexNum - 1) * 2); } } @Override public void attachToBody(Body body) { attachToBody(body, 0f, 0f, 0f); } /** * Attaches light to specified body with relative offset * * @param body * that will be automatically followed, note that the body * rotation angle is taken into account for the light offset * and direction calculations * @param offsetX * horizontal relative offset in world coordinates * @param offsetY * vertical relative offset in world coordinates * */ public void attachToBody(Body body, float offsetX, float offsetY) { attachToBody(body, offsetX, offsetY, 0f); } /** * Attaches light to specified body with relative offset and direction * * @param body * that will be automatically followed, note that the body * rotation angle is taken into account for the light offset * and direction calculations * @param offsetX * horizontal relative offset in world coordinates * @param offsetY * vertical relative offset in world coordinates * @param degrees * directional relative offset in degrees */ public void attachToBody(Body body, float offsetX, float offsetY, float degrees) { this.body = body; bodyOffsetX = offsetX; bodyOffsetY = offsetY; bodyAngleOffset = degrees; if (staticLight) dirty = true; } @Override public Vector2 getPosition() { tmpPosition.x = start.x; tmpPosition.y = start.y; return tmpPosition; } public Body getBody() { return body; } /** @return horizontal starting position of light in world coordinates **/ @Override public float getX() { return start.x; } /** @return vertical starting position of light in world coordinates **/ @Override public float getY() { return start.y; } @Override public void setPosition(float x, float y) { start.x = x; start.y = y; if (staticLight) dirty = true; } @Override public void setPosition(Vector2 position) { start.x = position.x; start.y = position.y; if (staticLight) dirty = true; } public boolean contains(Vector2 pos) { return contains(pos.x, pos.y); } @Override public boolean contains(float x, float y) { // fast fail final float x_d = start.x - x; final float y_d = start.y - y; final float dst2 = x_d * x_d + y_d * y_d; if (distance * distance <= dst2) return false; // actual check boolean oddNodes = false; float x2 = mx[rayNum] = start.x; float y2 = my[rayNum] = start.y; float x1, y1; for (int i = 0; i <= rayNum; x2 = x1, y2 = y1, ++i) { x1 = mx[i]; y1 = my[i]; if (((y1 < y) && (y2 >= y)) || (y1 >= y) && (y2 < y)) { if ((y - y1) / (y2 - y1) * (x2 - x1) < (x - x1)) oddNodes = !oddNodes; } } return oddNodes; } @Override protected void setRayNum(int rays) { super.setRayNum(rays); sin = new float[rays]; cos = new float[rays]; endX = new float[rays]; endY = new float[rays]; } protected boolean cull() { culled = rayHandler.culling && !rayHandler.intersect( start.x, start.y, distance + softShadowLength); return culled; } protected void updateBody() { if (body == null || staticLight) return; final Vector2 vec = body.getPosition(); float angle = body.getAngle(); final float cos = MathUtils.cos(angle); final float sin = MathUtils.sin(angle); final float dX = bodyOffsetX * cos - bodyOffsetY * sin; final float dY = bodyOffsetX * sin + bodyOffsetY * cos; start.x = vec.x + dX; start.y = vec.y + dY; setDirection(bodyAngleOffset + angle * MathUtils.radiansToDegrees); } protected void updateMesh() { for (int i = 0; i < rayNum; i++) { m_index = i; f[i] = 1f; tmpEnd.x = endX[i] + start.x; mx[i] = tmpEnd.x; tmpEnd.y = endY[i] + start.y; my[i] = tmpEnd.y; if (rayHandler.world != null && !xray && !rayHandler.pseudo3d) { rayHandler.world.rayCast(ray, start, tmpEnd); } } setMesh(); } protected void prepareFixtureData() { rayHandler.world.QueryAABB( dynamicShadowCallback, start.x - distance, start.y - distance, start.x + distance, start.y + distance); } protected void setMesh() { // ray starting point int size = 0; segments[size++] = start.x; segments[size++] = start.y; segments[size++] = colorF; segments[size++] = 1; // rays ending points. for (int i = 0; i < rayNum; i++) { segments[size++] = mx[i]; segments[size++] = my[i]; segments[size++] = colorF; segments[size++] = 1 - f[i]; } lightMesh.setVertices(segments, 0, size); if (!soft || xray || rayHandler.pseudo3d) return; size = 0; // rays ending points. for (int i = 0; i < rayNum; i++) { segments[size++] = mx[i]; segments[size++] = my[i]; segments[size++] = colorF; final float s = (1 - f[i]); segments[size++] = s; segments[size++] = mx[i] + s * softShadowLength * cos[i]; segments[size++] = my[i] + s * softShadowLength * sin[i]; segments[size++] = zeroColorBits; segments[size++] = 0f; } softShadowMesh.setVertices(segments, 0, size); } protected void updateDynamicShadowMeshes() { int meshInd = 0; float colBits = rayHandler.ambientLight.toFloatBits(); for (Fixture fixture : affectedFixtures) { Object userData = fixture.getUserData(); if (!(userData instanceof LightData) || fixture.isSensor()) continue; LightData data = (LightData) userData; int size = 0; float l; Shape fixtureShape = fixture.getShape(); Shape.Type type = fixtureShape.getType(); Body body = fixture.getBody(); center.set(body.getWorldCenter()); if (type == Shape.Type.Polygon || type == Shape.Type.Chain) { boolean isPolygon = (type == Shape.Type.Polygon); ChainShape cShape = isPolygon ? null : (ChainShape)fixtureShape; PolygonShape pShape = isPolygon ? (PolygonShape)fixtureShape : null; int vertexCount = isPolygon ? pShape.getVertexCount() : cShape.getVertexCount(); int minN = -1; int maxN = -1; int minDstN = -1; float minDst = Float.POSITIVE_INFINITY; boolean hasGasp = false; tmpVerts.clear(); for (int n = 0; n < vertexCount; n++) { if (isPolygon) { pShape.getVertex(n, tmpVec); } else { cShape.getVertex(n, tmpVec); } tmpVec.set(body.getWorldPoint(tmpVec)); tmpVerts.add(tmpVec.cpy()); tmpEnd.set(tmpVec).sub(start).limit2(0.0001f).add(tmpVec); if (fixture.testPoint(tmpEnd)) { if (minN == -1) minN = n; maxN = n; hasGasp = true; continue; } float currDist = tmpVec.dst2(start); if (currDist < minDst) { minDst = currDist; minDstN = n; } } ind.clear(); if (!hasGasp) { tmpVec.set(tmpVerts.get(minDstN)); for (int n = minDstN; n < vertexCount; n++) { ind.add(n); } for (int n = 0; n < minDstN; n++) { ind.add(n); } if (Intersector.pointLineSide(start, center, tmpVec) > 0) { ind.reverse(); ind.insert(0, ind.pop()); } } else if (minN == 0 && maxN == vertexCount - 1) { for (int n = maxN - 1; n > minN; n--) { ind.add(n); } } else { for (int n = minN - 1; n > -1; n--) { ind.add(n); } for (int n = vertexCount - 1; n > maxN ; n--) { ind.add(n); } } boolean contained = false; for (int n : ind.toArray()) { tmpVec.set(tmpVerts.get(n)); if (contains(tmpVec.x, tmpVec.y)){ contained = true; break; } } if (!contained) continue; for (int n : ind.toArray()) { tmpVec.set(tmpVerts.get(n)); float dst = tmpVec.dst(start); l = data.getLimit(dst, pseudo3dHeight, distance); tmpEnd.set(tmpVec).sub(start).setLength(l).add(tmpVec); float f1 = 1f - dst / distance; float f2 = 1f - (dst + l) / distance; tmpColor.set(Color.BLACK); float startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; tmpColor.set(Color.WHITE); float endColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f2).toFloatBits() : colBits; segments[size++] = tmpVec.x; segments[size++] = tmpVec.y; segments[size++] = startColBits; segments[size++] = f1; segments[size++] = tmpEnd.x; segments[size++] = tmpEnd.y; segments[size++] = endColBits; segments[size++] = f2; } if (data.roofShadow || pseudo3dHeight <= data.height) { for (int n = 0; n < vertexCount; n++) { tmpVec.set(tmpVerts.get(n)); float dst = tmpVec.dst(start); float f1 = 1f - dst / distance; tmpColor.set(Color.BLACK); float startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; segments[size++] = tmpVec.x; segments[size++] = tmpVec.y; segments[size++] = startColBits; segments[size++] = f1; if (n == vertexCount - 1) { tmpVec.set(tmpVerts.get(0)); dst = tmpVec.dst(start); f1 = 1f - dst / distance; tmpColor.set(Color.BLACK); startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; segments[size++] = tmpVec.x; segments[size++] = tmpVec.y; segments[size++] = startColBits; segments[size++] = f1; } } } } else if (type == Shape.Type.Circle) { CircleShape shape = (CircleShape)fixtureShape; float r = shape.getRadius(); if (!contains(tmpVec.set(center).add(r, r)) && !contains(tmpVec.set(center).add(-r, -r)) && !contains(tmpVec.set(center).add(r, -r)) && !contains(tmpVec.set(center).add(-r, r))) { continue; } float dst = tmpVec.set(center).dst(start); float a = (float) Math.acos(r/dst); l = data.getLimit(dst, pseudo3dHeight, distance); float f1 = 1f - dst / distance; float f2 = 1f - (dst + l) / distance; tmpColor.set(Color.BLACK); float startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; tmpColor.set(Color.WHITE); float endColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f2).toFloatBits() : colBits; tmpVec.set(start).sub(center).clamp(r, r).rotateRad(a); tmpStart.set(center).add(tmpVec); float angle = (MathUtils.PI2 - 2f * a) / RayHandler.CIRCLE_APPROX_POINTS; for (int k = 0; k < RayHandler.CIRCLE_APPROX_POINTS; k++) { tmpStart.set(center).add(tmpVec); segments[size++] = tmpStart.x; segments[size++] = tmpStart.y; segments[size++] = startColBits; segments[size++] = f1; tmpEnd.set(tmpStart).sub(start).setLength(l).add(tmpStart); segments[size++] = tmpEnd.x; segments[size++] = tmpEnd.y; segments[size++] = endColBits; segments[size++] = f2; tmpVec.rotateRad(angle); } } else if (type == Shape.Type.Edge) { EdgeShape shape = (EdgeShape)fixtureShape; shape.getVertex1(tmpVec); tmpVec.set(body.getWorldPoint(tmpVec)); if (!contains(tmpVec)) { continue; } float dst = tmpVec.dst(start); l = data.getLimit(dst, pseudo3dHeight, distance); float f1 = 1f - dst / distance; float f2 = 1f - (dst + l) / distance; tmpColor.set(Color.BLACK); float startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; tmpColor.set(Color.WHITE); float endColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f2).toFloatBits() : colBits; segments[size++] = tmpVec.x; segments[size++] = tmpVec.y; segments[size++] = startColBits; segments[size++] = f1; tmpEnd.set(tmpVec).sub(start).setLength(l).add(tmpVec); segments[size++] = tmpEnd.x; segments[size++] = tmpEnd.y; segments[size++] = endColBits; segments[size++] = f2; shape.getVertex2(tmpVec); tmpVec.set(body.getWorldPoint(tmpVec)); if (!contains(tmpVec)) { continue; } dst = tmpVec.dst(start); l = data.getLimit(dst, pseudo3dHeight, distance); f1 = 1f - dst / distance; f2 = 1f - (dst + l) / distance; tmpColor.set(Color.BLACK); startColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f1).toFloatBits() : oneColorBits; tmpColor.set(Color.WHITE); endColBits = rayHandler.shadowColorInterpolation ? tmpColor.lerp(rayHandler.ambientLight, f2).toFloatBits() : colBits; segments[size++] = tmpVec.x; segments[size++] = tmpVec.y; segments[size++] = startColBits; segments[size++] = f1; tmpEnd.set(tmpVec).sub(start).setLength(l).add(tmpVec); segments[size++] = tmpEnd.x; segments[size++] = tmpEnd.y; segments[size++] = endColBits; segments[size++] = f2; } Mesh mesh = null; if (meshInd >= dynamicShadowMeshes.size) { VertexDataType vertexDataType = VertexDataType.VertexArray; if (Gdx.gl30 != null) { vertexDataType = VertexDataType.VertexBufferObjectWithVAO; } mesh = new Mesh( vertexDataType, false, RayHandler.MAX_SHADOW_VERTICES, 0, new VertexAttribute(Usage.Position, 2, "vertex_positions"), new VertexAttribute(Usage.ColorPacked, 4, "quad_colors"), new VertexAttribute(Usage.Generic, 1, "s")); dynamicShadowMeshes.add(mesh); } else { mesh = dynamicShadowMeshes.get(meshInd); } mesh.setVertices(segments, 0, size); meshInd++; } dynamicShadowMeshes.truncate(meshInd); } public float getBodyOffsetX() { return bodyOffsetX; } public float getBodyOffsetY() { return bodyOffsetY; } public float getBodyAngleOffset() { return bodyAngleOffset; } public void setBodyOffsetX(float bodyOffsetX) { this.bodyOffsetX = bodyOffsetX; } public void setBodyOffsetY(float bodyOffsetY) { this.bodyOffsetY = bodyOffsetY; } public void setBodyAngleOffset(float bodyAngleOffset) { this.bodyAngleOffset = bodyAngleOffset; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy