-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #227 from xulman/addingArrows
Adding arrows
- Loading branch information
Showing
2 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package graphics.scenery | ||
|
||
import cleargl.GLVector | ||
import graphics.scenery.backends.ShaderType | ||
import java.nio.FloatBuffer | ||
import java.nio.IntBuffer | ||
|
||
/** | ||
* Class for creating 3D arrows, derived from [Node] and using [HasGeometry]. | ||
* The arrow is defined with [vector] having the vector's foot/start as the | ||
* reference coordinate (arrow's foot will appear at the coordinate given to setPosition()). | ||
* | ||
* /|\ | ||
* / | \ | ||
* /--+--\ | ||
* | | ||
* | | ||
* | | ||
* The arrow is created as the main segment (drawn vertically bottom to up), | ||
* then 3 segments to build a triangle and then another triangle perpendicular | ||
* to the former one; altogehter 7 segments drawn sequentially. | ||
* The arrow head scales with the length of the arrow. | ||
* | ||
* @author Vladimir Ulman <ulman@mpi-cbg.de> | ||
*/ | ||
class Arrow(var vector: GLVector = GLVector(0f,3)) : Node("Arrow"), HasGeometry { | ||
/** Size of one vertex (e.g. 3 in 3D) */ | ||
override val vertexSize: Int = 3 | ||
/** Size of one texcoord (e.g. 2 in 3D) */ | ||
override val texcoordSize: Int = 2 | ||
/** Geometry type -- Default for Line is [GeometryType.LINE] */ | ||
override var geometryType: GeometryType = GeometryType.LINE_STRIP_ADJACENCY | ||
/** Vertex buffer */ | ||
override var vertices: FloatBuffer = BufferUtils.allocateFloat(30) | ||
/** Normal buffer */ | ||
override var normals: FloatBuffer = BufferUtils.allocateFloat(30) | ||
/** Texcoord buffer */ | ||
override var texcoords: FloatBuffer = BufferUtils.allocateFloat(20) | ||
/** Index buffer */ | ||
override var indices: IntBuffer = IntBuffer.wrap(intArrayOf()) | ||
|
||
/** Shader property for the line's starting segment color. Consumed by the renderer. */ | ||
@ShaderProperty | ||
var startColor = GLVector(0.0f, 1.0f, 0.0f, 1.0f) | ||
|
||
/** Shader property for the line's color. Consumed by the renderer. */ | ||
@ShaderProperty | ||
var lineColor = GLVector(1.0f, 1.0f, 1.0f, 1.0f) | ||
|
||
/** Shader property for the line's end segment color. Consumed by the renderer. */ | ||
@ShaderProperty | ||
var endColor = GLVector(0.7f, 0.5f, 0.5f, 1.0f) | ||
|
||
/** Shader property for the line's cap length (start and end caps). Consumed by the renderer. */ | ||
@ShaderProperty | ||
var capLength = 1 | ||
|
||
/** Shader property to keep track of the current number of vertices. Consumed by the renderer. */ | ||
@ShaderProperty | ||
var vertexCount: Int = 0 | ||
private set | ||
|
||
/** Shader property for the line's edge width. Consumed by the renderer. */ | ||
@ShaderProperty | ||
var edgeWidth = 2.0f | ||
|
||
//shortcut to null vector... to prevent from creating it anew with every call to reshape() | ||
private val zeroGLvec = GLVector(0.0f, 0.0f, 0.0f) | ||
|
||
init { | ||
material = ShaderMaterial.fromClass(Line::class.java, listOf(ShaderType.VertexShader, ShaderType.GeometryShader, ShaderType.FragmentShader)) | ||
material.cullingMode = Material.CullingMode.None | ||
|
||
reshape(vector) | ||
} | ||
|
||
/** | ||
* Changes the shape of this arrow. | ||
* | ||
* @param vector The vector defining the shape of the arrow | ||
*/ | ||
fun reshape(vector: GLVector) { | ||
//init the data structures | ||
clearPoints() | ||
|
||
/** create the vector shape */ | ||
//first of the two mandatory surrounding fake points that are never displayed | ||
addPoint(zeroGLvec) | ||
|
||
//the main "vertical" segment of the vector | ||
addPoint(zeroGLvec) | ||
addPoint(vector) | ||
|
||
//the "horizontal" base segment of the "arrow head" triangles | ||
var base = if (vector.x() == 0.0f && vector.y() == 0.0f) { | ||
//the input 'vector' must be parallel to the z-axis, | ||
//we can use this particular 'base' then | ||
GLVector(0.0f, 1.0f, 0.0f) | ||
} | ||
else { | ||
//vector 'base' is perpendicular to the input 'vector' | ||
GLVector(-vector.y(), vector.x(), 0.0f).normalize() | ||
} | ||
|
||
//the width of the "arrow head" triangle | ||
val v = 0.1f * vector.magnitude() | ||
|
||
//the first triangle: | ||
base = base.times(v) | ||
addPoint(vector.times(0.8f).plus(base)) | ||
addPoint(vector.times(0.8f).minus(base)) | ||
addPoint(vector) | ||
//NB: the 0.8f defines the height (1-0.8) of the "arrow head" triangle | ||
|
||
//the second triangle: | ||
base = base.cross(vector).normalize().times(v) | ||
addPoint(vector.times(0.8f).plus(base)) | ||
addPoint(vector.times(0.8f).minus(base)) | ||
addPoint(vector) | ||
|
||
//second of the two mandatory surrounding fake points that are never displayed | ||
addPoint(vector) | ||
} | ||
|
||
private fun addPoint(p: GLVector) { | ||
vertices.position(vertices.limit()) | ||
vertices.limit(vertices.limit() + 3) | ||
vertices.put(p.toFloatArray()) | ||
vertices.flip() | ||
|
||
normals.position(normals.limit()) | ||
normals.limit(normals.limit() + 3) | ||
normals.put(p.toFloatArray()) | ||
normals.flip() | ||
|
||
texcoords.position(texcoords.limit()) | ||
texcoords.limit(texcoords.limit() + 2) | ||
texcoords.put(0.225f) | ||
texcoords.put(0.225f) | ||
texcoords.flip() | ||
|
||
dirty = true | ||
vertexCount = vertices.limit()/vertexSize | ||
|
||
boundingBox = generateBoundingBox() | ||
} | ||
|
||
private fun clearPoints() { | ||
vertices.clear() | ||
normals.clear() | ||
texcoords.clear() | ||
|
||
vertices.limit(0) | ||
normals.limit(0) | ||
texcoords.limit(0) | ||
} | ||
} |
135 changes: 135 additions & 0 deletions
135
src/test/tests/graphics/scenery/tests/examples/basic/ArrowExample.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package graphics.scenery.tests.examples.basic | ||
|
||
import cleargl.GLVector | ||
import graphics.scenery.* | ||
import graphics.scenery.backends.Renderer | ||
import org.junit.Test | ||
import kotlin.concurrent.thread | ||
import kotlin.math.PI | ||
import kotlin.math.cos | ||
import kotlin.math.sin | ||
|
||
/** | ||
* Simple example to demonstrate the drawing of 3D arrows/vectors. | ||
* | ||
* This example will draw a circle made of vectors using | ||
* the [Arrow] class. One vector will, however, always | ||
* illuminate differently, and the position of such will | ||
* circulate... | ||
* | ||
* @author Vladimir Ulman <ulman@mpi-cbg.de> | ||
*/ | ||
class ArrowExample : SceneryBase("ArrowExample") { | ||
|
||
override fun init() { | ||
renderer = Renderer.createRenderer(hub, applicationName, scene, windowWidth, windowHeight) | ||
hub.add(SceneryElement.Renderer, renderer!!) | ||
|
||
setupScene() | ||
useScene() | ||
} | ||
|
||
|
||
private fun setupScene() { | ||
//boundaries of our world | ||
val hull = Box(GLVector(50.0f, 50.0f, 50.0f), insideNormals = true) | ||
hull.material.diffuse = GLVector(0.2f, 0.2f, 0.2f) | ||
hull.material.cullingMode = Material.CullingMode.Front | ||
scene.addChild(hull) | ||
|
||
//lights and camera | ||
var pl = emptyArray<PointLight>() | ||
for (i in 0..3) | ||
{ | ||
val l = PointLight(radius = 200.0f) | ||
l.intensity = 600.0f | ||
l.emissionColor = GLVector(1.0f,3) | ||
|
||
scene.addChild(l) | ||
pl = pl.plus(l) | ||
} | ||
pl[0].position = GLVector(0f,10f,0f) | ||
pl[1].position = GLVector(0f,-10f,0f) | ||
pl[2].position = GLVector(-10f,0f,0f) | ||
pl[3].position = GLVector(10f,0f,0f) | ||
|
||
val cam: Camera = DetachedHeadCamera() | ||
cam.position = GLVector(0.0f, 0.0f, 15.0f) | ||
cam.perspectiveCamera(50.0f, windowWidth.toFloat(), windowHeight.toFloat()) | ||
cam.active = true | ||
scene.addChild(cam) | ||
} | ||
|
||
|
||
private fun useScene() { | ||
//we shall have faint and bright vectors... | ||
val matBright = Material() | ||
matBright.diffuse = GLVector(0.0f, 1.0f, 0.0f) | ||
matBright.ambient = GLVector(1.0f, 1.0f, 1.0f) | ||
matBright.specular = GLVector(1.0f, 1.0f, 1.0f) | ||
matBright.cullingMode = Material.CullingMode.None | ||
|
||
val matFaint = Material() | ||
matFaint.diffuse = GLVector(0.0f, 0.6f, 0.6f) | ||
matFaint.ambient = GLVector(1.0f, 1.0f, 1.0f) | ||
matFaint.specular = GLVector(1.0f, 1.0f, 1.0f) | ||
matFaint.cullingMode = Material.CullingMode.None | ||
|
||
//... arranged along a circle | ||
val circleCentre = GLVector(0.0f,3) | ||
val circleRadius = 6.0f | ||
|
||
val arrowsInCircle = 30 | ||
|
||
|
||
//create the circle of vectors | ||
var al = emptyArray<Arrow>() | ||
var lastPos = GLVector(circleRadius,0f,0f) | ||
var currPos : GLVector | ||
for (i in 1..arrowsInCircle) | ||
{ | ||
val curAng = (i*2.0f* PI/arrowsInCircle).toFloat() | ||
currPos = GLVector(circleRadius*cos(curAng) +circleCentre.x(), | ||
circleRadius*sin(curAng) +circleCentre.y(), | ||
circleCentre.z()) | ||
|
||
// ========= this is how you create an Arrow ========= | ||
val a = Arrow(currPos.minus(lastPos)) //shape of the vector itself | ||
a.position = lastPos //position/base of the vector | ||
a.material = matFaint //usual stuff follows... | ||
a.edgeWidth = 0.5f | ||
scene.addChild(a) | ||
|
||
// if you want to change the shape and position of the vector later, | ||
// you can use this (instead of creating a new vector) | ||
// a.reshape( newVector ) | ||
// a.position = newBase | ||
// ========= this is how you create an Arrow ========= | ||
|
||
al = al.plus(a) | ||
|
||
lastPos = currPos | ||
} | ||
|
||
|
||
//finally, have some fun... | ||
thread { | ||
var i = 0 | ||
while (true) { | ||
al[i].material = matFaint | ||
i = (i+1).rem(arrowsInCircle) | ||
al[i].material = matBright | ||
|
||
Thread.sleep(150) | ||
} | ||
} | ||
} | ||
|
||
override fun inputSetup() { | ||
setupCameraModeSwitching(keybinding = "C") | ||
} | ||
|
||
@Test override fun main() { | ||
super.main() | ||
} | ||
} |