ios - SceneKit - Draw 3D Parabola -
i'm given 3 points , need draw smooth 3d parabola. trouble curved line choppy , has weird divots in it
here code...
func drawjump(jump: jump){ let halfdistance = jump.distance.floatvalue/2 float let tup = calcparabolavalues(0.0, y1: 0.0, x2: halfdistance, y2: jump.height.floatvalue, x3: jump.distance.floatvalue, y3: 0) println("tuple \tup") var currentx = 0 float var increment = jump.distance.floatvalue / float(50) while currentx < jump.distance.floatvalue - increment { let x1 = float(currentx) let x2 = float((currentx+increment)) let y1 = calcparabolayval(tup.a, b: tup.b, c: tup.c, x: x1) let y2 = calcparabolayval(tup.a, b: tup.b, c: tup.c, x: x2) drawline(x1, y1: y1, x2: x2, y2: y2) currentx += increment } } func calcparabolavalues(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float) -> (a: float, b: float, c: float) { println(x1, y1, x2, y2, x3, y3) let = y1/((x1-x2)*(x1-x3)) + y2/((x2-x1)*(x2-x3)) + y3/((x3-x1)*(x3-x2)) let b = (-y1*(x2+x3)/((x1-x2)*(x1-x3))-y2*(x1+x3)/((x2-x1)*(x2-x3))-y3*(x1+x2)/((x3-x1)*(x3-x2))) let c = (y1*x2*x3/((x1-x2)*(x1-x3))+y2*x1*x3/((x2-x1)*(x2-x3))+y3*x1*x2/((x3-x1)*(x3-x2))) return (a, b, c) } func calcparabolayval(a:float, b:float, c:float, x:float)->float{ return * x * x + b * x + c } func drawline(x1: float, y1: float,x2: float, y2: float) { println("drawline \(x1) \(y1) \(x2) \(y2)") let positions: [float32] = [ x1, y1, 0, x2, y2, 0 ] let positiondata = nsdata(bytes: positions, length: sizeof(float32)*positions.count) let indices: [int32] = [0, 1] let indexdata = nsdata(bytes: indices, length: sizeof(int32) * indices.count) let source = scngeometrysource(data: positiondata, semantic: scngeometrysourcesemanticvertex, vectorcount: indices.count, floatcomponents: true, componentspervector: 3, bytespercomponent: sizeof(float32), dataoffset: 0, datastride: sizeof(float32) * 3) let element = scngeometryelement(data: indexdata, primitivetype: scngeometryprimitivetype.line, primitivecount: indices.count, bytesperindex: sizeof(int32)) let line = scngeometry(sources: [source], elements: [element]) self.rootnode.addchildnode( scnnode(geometry: line)) } func renderer(arenderer: scnscenerenderer, willrenderscene scene: scnscene, attime time: nstimeinterval) { gllinewidth(20) }
i have figure out how animate arc left right. can me out? swift or objective c fine. appreciated. thanks!
i'd recommend using scnshape
create parabola. start, you'll need represent parabola bézier curve. can use uibezierpath
that. animation, find shader modifiers nice fit cases this.
the parabola
watch out, though — want path represents open stroke of arc. if this:
let path = uibezierpath() path.movetopoint(cgpointzero) path.addquadcurvetopoint(cgpoint(x: 100, y: 0), controlpoint: cgpoint(x: 50, y: 200))
you'll filled-in parabola, (seen in 2d in debugger quick look, extruded in 3d scnshape
):
to create closed shape that's arc, you'll need trace on curve, little bit away original:
let path = uibezierpath() path.movetopoint(cgpointzero) path.addquadcurvetopoint(cgpoint(x: 100, y: 0), controlpoint: cgpoint(x: 50, y: 200)) path.addlinetopoint(cgpoint(x: 99, y: 0)) path.addquadcurvetopoint(cgpoint(x: 1, y: 0), controlpoint: cgpoint(x: 50, y: 198))
that's better.
... in three-dee!
how make 3d? make scnshape
extrusion depth like:
let shape = scnshape(path: path, extrusiondepth: 10)
and set in scene:
shape.firstmaterial?.diffuse.contents = skcolor.bluecolor() let shapenode = scnnode(geometry: shape) shapenode.pivot = scnmatrix4maketranslation(50, 0, 0) shapenode.eulerangles.y = float(-m_pi_4) root.addchildnode(shapenode)
here i'm using pivot
make shape rotate around major axis of parabola, instead of y = 0
axis of planar bézier curve. , making blue. also, root
shortcut made view's scene's root node.
animating
the shape of parabola doesn't need change through animation — need visual effect progressively reveals along x-axis. shader modifiers great fit that, because can make animated effect per-pixel instead of per-vertex , expensive work on gpu.
here's shader snippet uses progress
parameter, varying 0 1, set opacity based on x-position:
// declare variable can set scenekit code uniform float progress; // tell scenekit shader uses transparency correct draw order #pragma transparent // position in model space vec4 mpos = u_inversemodelviewtransform * vec4(_surface.position, 1.0); // bit of math ramp alpha based on progress-adjusted position _surface.transparent.a = clamp(1.0 - ((mpos.x + 50.0) - progress * 200.0) / 50.0, 0.0, 1.0);
set shader modifier surface entry point, , can animate progress
variable:
let modifier = "uniform float progress;\n #pragma transparent\n vec4 mpos = u_inversemodelviewtransform * vec4(_surface.position, 1.0);\n _surface.transparent.a = clamp(1.0 - ((mpos.x + 50.0) - progress * 200.0) / 50.0, 0.0, 1.0);" shape.shadermodifiers = [ scnshadermodifierentrypointsurface: modifier ] shape.setvalue(0.0, forkey: "progress") scntransaction.begin() scntransaction.setanimationduration(10) shape.setvalue(1.0, forkey: "progress") scntransaction.commit()
further considerations
here's whole thing in form can paste (ios) playground. few things left exercises reader, plus other notes:
factor out magic numbers , make function or class can alter size/shape of parabola. (remember can scale scenekit nodes relative other scene elements, don't have use same units.)
position parabola relative other scene elements. if take away line sets
pivot
,shapenode.position
left end of parabola. change parabola's length (or scale it), rotate around y-axis, , can make other end line other node. (for fire ze missiles at?)i threw swift 2 beta, don't think there's swift-2-specific syntax in there — porting 1.2 if need deploy should straightforward.
if want on os x, it's bit trickier — there,
scnshape
usesnsbezierpath
, unlikeuibezierpath
doesn't support quadratic curves. easy way out fake elliptical arc.
Comments
Post a Comment