## PA1 ## Joining Cubic Bezier Curves with G1 Continuity ## Sample Tcl Program prepared by Jane Yen (modified by Carlo Sequin) ## ## Modify only the geometry section that calculates the control points P2 and P3. ## Construct your own pleasing interpolation function. ################################################################################ tclinit { set winName .slfWINDOW source SLIDEUI.tcl source MATH.tcl ############################################################################### ### Create Sliders ############################################################################### proc CreateBezierUI { parent name } { set subname "slf_[subst \$name]" if { \$parent == {} } { set root .\$subname } elseif { \$parent == "." } { set root .\$subname } else { set root \$parent.\$subname } toplevel \$root wm geometry \$root +50+150 # These sliders change the geometry and thus will call the update function set dist [CreateScaleCmd \$name \$root dist "distance" 0.5 -1 2 0.05 1 horizontal update] set angl [CreateScaleCmd \$name \$root angl "direction" 0.5 -1 2 0.05 1 horizontal update] set slices [CreateScale \$name \$root slices "slices" 15 2 50 1 1 horizontal] set drawc [CreateScale \$name \$root drawc "draw controls" 1 0 1 1 1 horizontal] set debug [CreateScale \$name \$root debug "debug" 0 0 1 1 1 horizontal] pack \$dist \$angl \$slices \$drawc \$debug -side top -fill x } CreateBezierUI \$winName bez } tclinit { ############################################################################### ### Initialize Points (do not modify) ############################################################################### # 15 points to be interpolated global p1x p1y p2x p2y p3x p3y p4x p4y p5x p5y p6x p6y p7x p7y p8x p8y p9x p9y global p10x p10y p11x p11y p12x p12y p13x p13y p14x p14y p15x p15y global p0x p0y p16x p16y p17x p17y set p1x 7.3 set p1y -2.2 set p2x 4.8 set p2y -0.7 set p3x 0.5 set p3y -0.6 set p4x 1.2 set p4y 0.5 set p5x -0.5 set p5y 5.0 set p6x -0.5 set p6y 4.5 set p7x -3.0 set p7y 2.3 set p8x -0.6 set p8y -0.3 set p9x -5.4 set p9y -1.1 set p10x -8.4 set p10y -3.2 set p11x -6.0 set p11y -4.4 set p12x -5.9 set p12y -4.8 set p13x -0.6 set p13y -2.2 set p14x 2.8 set p14y -3.7 set p15x 5.5 set p15y -3.8 set p0x [expr \$p15x] set p0y [expr \$p15y] set p16x [expr \$p1x] set p16y [expr \$p1y] set p17x [expr \$p2x] set p17y [expr \$p2y] # intermediate bezier control points set ptlist {b1_2x b1_2y \ b2_2x b2_2y \ b3_2x b3_2y \ b4_2x b4_2y \ b5_2x b5_2y \ b6_2x b6_2y \ b7_2x b7_2y \ b8_2x b8_2y \ b9_2x b9_2y \ b10_2x b10_2y \ b11_2x b11_2y \ b12_2x b12_2y \ b13_2x b13_2y \ b14_2x b14_2y \ b15_2x b15_2y \ b1_3x b1_3y \ b2_3x b2_3y \ b3_3x b3_3y \ b4_3x b4_3y \ b5_3x b5_3y \ b6_3x b6_3y \ b7_3x b7_3y \ b8_3x b8_3y \ b9_3x b9_3y \ b10_3x b10_3y \ b11_3x b11_3y \ b12_3x b12_3y \ b13_3x b13_3y \ b14_3x b14_3y \ b15_3x b15_3y} foreach pt \$ptlist { global \$pt set \$pt 0 } } tclinit { ############################################################################### ### Calculate the 2nd intermediate bezier control points ############################################################################### proc calculate_pt2 { } { global p0x p0y p1x p1y p2x p2y p3x p3y p4x p4y p5x p5y p6x p6y p7x p7y p8x p8y p9x p9y global p10x p10y p11x p11y p12x p12y p13x p13y p14x p14y p15x p15y p16x p16y p17x p17y global b1_2x b1_2y b2_2x b2_2y b3_2x b3_2y b4_2x b4_2y b5_2x b5_2y b6_2x b6_2y global b7_2x b7_2y b8_2x b8_2y b9_2x b9_2y b10_2x b10_2y b11_2x b11_2y b12_2x b12_2y global b13_2x b13_2y b14_2x b14_2y b15_2x b15_2y global bez_dist bez_angl bez_debug # for each bezier curve for {set i 1} {\$i <= 15} {incr i} { if { \$bez_debug } { puts "" } ################################################ # get points i-1, i, i+1 # point i-1 set name p[subst [expr \$i - 1]]x eval "set pt0x \$\$name" set name p[subst [expr \$i - 1]]y eval "set pt0y \$\$name" if { \$bez_debug } { puts "pt0 = (\$pt0x \$pt0y)" } # point i set name p[subst [expr \$i - 0]]x eval "set pt1x \$\$name" set name p[subst [expr \$i - 0]]y eval "set pt1y \$\$name" if { \$bez_debug } { puts "pt1 = (\$pt1x \$pt1y)" } # point i + 1 set name p[subst [expr \$i + 1]]x eval "set pt2x \$\$name" set name p[subst [expr \$i + 1]]y eval "set pt2y \$\$name" if { \$bez_debug } { puts "pt2 = (\$pt2x \$pt2y)" } ################################################ # calculate point pi_2 ## THIS IS THE SECTION YOU SHOULD MODIFY ! # Most of the elements that you need are already given # Combine and use them in a suitable way. # (The current formulation is just an arbitrary place holder). ################################################ ### midpoint of pt0 and pt2 set pt02x [expr (\$pt0x + \$pt2x) / 2.0] set pt02y [expr (\$pt0y + \$pt2y) / 2.0] if { \$bez_debug } { puts "pt02 = (\$pt02x \$pt02y)" } ### vector pt1 - pt0 along incoming control segment set v01x [expr \$pt1x - \$pt0x] set v01y [expr \$pt1y - \$pt0y] if { \$bez_debug } { puts "v = (\$v01x \$v01y)" } ### normalize set len01 [expr sqrt(\$v01x*\$v01x + \$v01y*\$v01y)] if { \$bez_debug } { puts "len01 = \$len01" } set nv01x [expr \$v01x/\$len01] set nv01y [expr \$v01y/\$len01] ### vector pt2 - pt1 along outgoing control segment set v12x [expr \$pt2x - \$pt1x] set v12y [expr \$pt2y - \$pt1y] if { \$bez_debug } { puts "v = (\$v12x \$v12y)" } ### normalize set len12 [expr sqrt(\$v12x*\$v12x + \$v12y*\$v12y)] if { \$bez_debug } { puts "len12 = \$len12" } set nv12x [expr \$v12x/\$len12] set nv12y [expr \$v12y/\$len12] ### an averaged vector between the above two directions set vavx [expr (1-\$bez_angl)*\$nv01x + \$bez_angl*\$nv12x] set vavy [expr (1-\$bez_angl)*\$nv01y + \$bez_angl*\$nv12y] ### normalize set lenav [expr sqrt(\$vavx*\$vavx + \$vavy*\$vavy)] if { \$bez_debug } { puts "lenav = \$lenav" } set nvavx [expr \$vavx/\$lenav] set nvavy [expr \$vavy/\$lenav] ### vector pt2 - pt0 set v02x [expr \$pt2x - \$pt0x] set v02y [expr \$pt2y - \$pt0y] if { \$bez_debug } { puts "v = (\$v02x \$v02y)" } ### normalize set len02 [expr sqrt(\$v02x*\$v02x + \$v02y*\$v02y)] if { \$bez_debug } { puts "len02 = \$len02" } set nv02x [expr \$v02x/\$len02] set nv02y [expr \$v02y/\$len02] ## Now put it all together -- somehow: ### calculate pt2 of the Bezier Curve (pt1 pt2 pt3 pt4) #set x [expr \$pt1x + \$bez_dist*\$v02x] #set y [expr \$pt1y + \$bez_dist*\$v02y] set x [expr \$pt1x + \$bez_dist*\$nvavx*\$len12] set y [expr \$pt1y + \$bez_dist*\$nvavy*\$len12] set b[subst \$i]_2x [expr \$x] set b[subst \$i]_2y [expr \$y] if { \$bez_debug } { puts "b[expr \$i]_2 (\$x \$y)" } } } ############################################################################### ### Calculate the 3rd intermediate bezier control points ############################################################################### proc calculate_pt3 { } { global p0x p0y p1x p1y p2x p2y p3x p3y p4x p4y p5x p5y p6x p6y p7x p7y p8x p8y p9x p9y global p10x p10y p11x p11y p12x p12y p13x p13y p14x p14y p15x p15y p16x p16y p17x p17y global b1_3x b1_3y b2_3x b2_3y b3_3x b3_3y b4_3x b4_3y b5_3x b5_3y b6_3x b6_3y global b7_3x b7_3y b8_3x b8_3y b9_3x b9_3y b10_3x b10_3y b11_3x b11_3y b12_3x b12_3y global b13_3x b13_3y b14_3x b14_3y b15_3x b15_3y global bez_dist bez_angl bez_debug # for each bezier curve for {set i 1} {\$i <= 15} {incr i} { if { \$bez_debug } { puts "" } ################################################ # get points i, i+1, i+2 # point i set name p[subst [expr \$i - 0]]x eval "set pt0x \$\$name" set name p[subst [expr \$i - 0]]y eval "set pt0y \$\$name" if { \$bez_debug } { puts "pt0 = (\$pt0x \$pt0y)" } # point i + 1 set name p[subst [expr \$i + 1]]x eval "set pt1x \$\$name" set name p[subst [expr \$i + 1]]y eval "set pt1y \$\$name" if { \$bez_debug } { puts "pt1 = (\$pt1x \$pt1y)" } # point i + 2 set name p[subst [expr \$i + 2]]x eval "set pt2x \$\$name" set name p[subst [expr \$i + 2]]y eval "set pt2y \$\$name" if { \$bez_debug } { puts "pt2 = (\$pt2x \$pt2y)" } ################################################ # calculate point pi_3 ## THIS IS THE SECTION YOU MAY MODIFY ! # The same basic pieces as above are given # But a different formulation from pi_2 is used, giving not even G1 continuity. # It is probably a good idea to use the SAME basic formulation for both points. ################################################ ### midpoint of pt0 and pt2 set pt02x [expr (\$pt0x + \$pt2x) / 2.0] set pt02y [expr (\$pt0y + \$pt2y) / 2.0] if { \$bez_debug } { puts "pt02 = (\$pt02x \$pt02y)" } ### vector pt1 - pt0 along incoming control segment set v01x [expr \$pt1x - \$pt0x] set v01y [expr \$pt1y - \$pt0y] if { \$bez_debug } { puts "v = (\$v01x \$v01y)" } ### normalize set len01 [expr sqrt(\$v01x*\$v01x + \$v01y*\$v01y)] if { \$bez_debug } { puts "len01 = \$len01" } set nv01x [expr \$v01x/\$len01] set nv01y [expr \$v01y/\$len01] ### vector pt2 - pt1 along outgoing control segment set v12x [expr \$pt2x - \$pt1x] set v12y [expr \$pt2y - \$pt1y] if { \$bez_debug } { puts "v = (\$v12x \$v12y)" } ### normalize set len12 [expr sqrt(\$v12x*\$v12x + \$v12y*\$v12y)] if { \$bez_debug } { puts "len12 = \$len12" } set nv12x [expr \$v12x/\$len12] set nv12y [expr \$v12y/\$len12] ### an averaged vector between the above two directions set vavx [expr (1-\$bez_angl)*\$nv01x + \$bez_angl*\$nv12x] set vavy [expr (1-\$bez_angl)*\$nv01y + \$bez_angl*\$nv12y] ### normalize set lenav [expr sqrt(\$vavx*\$vavx + \$vavy*\$vavy)] if { \$bez_debug } { puts "lenav = \$lenav" } set nvavx [expr \$vavx/\$lenav] set nvavy [expr \$vavy/\$lenav] ### vector pt2 - pt0 set v02x [expr \$pt2x - \$pt0x] set v02y [expr \$pt2y - \$pt0y] if { \$bez_debug } { puts "v = (\$v02x \$v02y)" } ### normalize set len02 [expr sqrt(\$v02x*\$v02x + \$v02y*\$v02y)] if { \$bez_debug } { puts "len02 = \$len02" } set nv02x [expr \$v02x/\$len02] set nv02y [expr \$v02y/\$len02] ## Now put it all together -- somehow: ### calculate pt2 of the Bezier Curve (pt1 pt2 pt3 pt4) #set x [expr \$pt1x - \$bez_dist*\$v02x] #set y [expr \$pt1y - \$bez_dist*\$v02y] set x [expr \$pt1x - \$bez_dist*\$nvavx*\$len01] set y [expr \$pt1y - \$bez_dist*\$nvavy*\$len01] set b[subst \$i]_3x [expr \$x] set b[subst \$i]_3y [expr \$y] if { \$bez_debug } { puts "b[expr \$i]_3 (\$x \$y)" } } } ############################################################################### ### Update gets called when the "distance" slider variable changes. ### This will recompute the intermediate control points ############################################################################### proc update { foo } { global bez_debug if { \$bez_debug } { puts "----------------- Calculate Point 2" } calculate_pt2 if { \$bez_debug } { puts "----------------- Calculate Point 3" } calculate_pt3 } } ######################################################################################## ### Nothing below this line needs to be modified ######################################################################################## #################### # SURFACES #################### surface sGrey color (0.25 0.25 0.25) endsurface surface sBlue color (0.25 0.25 0.8) endsurface surface sRed color (0.8 0.25 0.25) endsurface surface sYellow color (0.8 0.8 0.25) endsurface #################### # Objects #################### sphere sph radius 0.1 endsphere group points_to_interpolate instance sph surface sRed endinstance instance sph #1 translate ({expr \$p1x} {expr \$p1y} 0) endinstance instance sph #2 translate ({expr \$p2x} {expr \$p2y} 0) endinstance instance sph #3 translate ({expr \$p3x} {expr \$p3y} 0) endinstance instance sph #4 translate ({expr \$p4x} {expr \$p4y} 0) endinstance instance sph #5 translate ({expr \$p5x} {expr \$p5y} 0) endinstance instance sph #6 translate ({expr \$p6x} {expr \$p6y} 0) endinstance instance sph #7 translate ({expr \$p7x} {expr \$p7y} 0) endinstance instance sph #8 translate ({expr \$p8x} {expr \$p8y} 0) endinstance instance sph #9 translate ({expr \$p9x} {expr \$p9y} 0) endinstance instance sph #10 translate ({expr \$p10x} {expr \$p10y} 0) endinstance instance sph #11 translate ({expr \$p11x} {expr \$p11y} 0) endinstance instance sph #12 translate ({expr \$p12x} {expr \$p12y} 0) endinstance instance sph #13 translate ({expr \$p13x} {expr \$p13y} 0) endinstance instance sph #14 translate ({expr \$p14x} {expr \$p14y} 0) endinstance instance sph #15 translate ({expr \$p15x} {expr \$p15y} 0) endinstance endgroup ####################### # Cubic Bezier Curves ####################### point b1_1 ( {expr \$p1x} {expr \$p1y} 0 ) endpoint point b1_2 ( {expr \$b1_2x} {expr \$b1_2y} 0 ) endpoint point b1_3 ( {expr \$b1_3x} {expr \$b1_3y} 0 ) endpoint point b1_4 ( {expr \$p2x} {expr \$p2y} 0 ) endpoint beziercurve bz1 controlpointlist (b1_1 b1_2 b1_3 b1_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b2_1 ( {expr \$p2x} {expr \$p2y} 0 ) endpoint point b2_2 ( {expr \$b2_2x} {expr \$b2_2y} 0 ) endpoint point b2_3 ( {expr \$b2_3x} {expr \$b2_3y} 0 ) endpoint point b2_4 ( {expr \$p3x} {expr \$p3y} 0 ) endpoint beziercurve bz2 controlpointlist (b2_1 b2_2 b2_3 b2_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b3_1 ( {expr \$p3x} {expr \$p3y} 0 ) endpoint point b3_2 ( {expr \$b3_2x} {expr \$b3_2y} 0 ) endpoint point b3_3 ( {expr \$b3_3x} {expr \$b3_3y} 0 ) endpoint point b3_4 ( {expr \$p4x} {expr \$p4y} 0 ) endpoint beziercurve bz3 controlpointlist (b3_1 b3_2 b3_3 b3_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b4_1 ( {expr \$p4x} {expr \$p4y} 0 ) endpoint point b4_2 ( {expr \$b4_2x} {expr \$b4_2y} 0 ) endpoint point b4_3 ( {expr \$b4_3x} {expr \$b4_3y} 0 ) endpoint point b4_4 ( {expr \$p5x} {expr \$p5y} 0 ) endpoint beziercurve bz4 controlpointlist (b4_1 b4_2 b4_3 b4_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b5_1 ( {expr \$p5x} {expr \$p5y} 0 ) endpoint point b5_2 ( {expr \$b5_2x} {expr \$b5_2y} 0 ) endpoint point b5_3 ( {expr \$b5_3x} {expr \$b5_3y} 0 ) endpoint point b5_4 ( {expr \$p6x} {expr \$p6y} 0 ) endpoint beziercurve bz5 controlpointlist (b5_1 b5_2 b5_3 b5_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b6_1 ( {expr \$p6x} {expr \$p6y} 0 ) endpoint point b6_2 ( {expr \$b6_2x} {expr \$b6_2y} 0 ) endpoint point b6_3 ( {expr \$b6_3x} {expr \$b6_3y} 0 ) endpoint point b6_4 ( {expr \$p7x} {expr \$p7y} 0 ) endpoint beziercurve bz6 controlpointlist (b6_1 b6_2 b6_3 b6_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b7_1 ( {expr \$p7x} {expr \$p7y} 0 ) endpoint point b7_2 ( {expr \$b7_2x} {expr \$b7_2y} 0 ) endpoint point b7_3 ( {expr \$b7_3x} {expr \$b7_3y} 0 ) endpoint point b7_4 ( {expr \$p8x} {expr \$p8y} 0 ) endpoint beziercurve bz7 controlpointlist (b7_1 b7_2 b7_3 b7_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b8_1 ( {expr \$p8x} {expr \$p8y} 0 ) endpoint point b8_2 ( {expr \$b8_2x} {expr \$b8_2y} 0 ) endpoint point b8_3 ( {expr \$b8_3x} {expr \$b8_3y} 0 ) endpoint point b8_4 ( {expr \$p9x} {expr \$p9y} 0 ) endpoint beziercurve bz8 controlpointlist (b8_1 b8_2 b8_3 b8_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b9_1 ( {expr \$p9x} {expr \$p9y} 0 ) endpoint point b9_2 ( {expr \$b9_2x} {expr \$b9_2y} 0 ) endpoint point b9_3 ( {expr \$b9_3x} {expr \$b9_3y} 0 ) endpoint point b9_4 ( {expr \$p10x} {expr \$p10y} 0 ) endpoint beziercurve bz9 controlpointlist (b9_1 b9_2 b9_3 b9_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b10_1 ( {expr \$p10x} {expr \$p10y} 0 ) endpoint point b10_2 ( {expr \$b10_2x} {expr \$b10_2y} 0 ) endpoint point b10_3 ( {expr \$b10_3x} {expr \$b10_3y} 0 ) endpoint point b10_4 ( {expr \$p11x} {expr \$p11y} 0 ) endpoint beziercurve bz10 controlpointlist (b10_1 b10_2 b10_3 b10_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b11_1 ( {expr \$p11x} {expr \$p11y} 0 ) endpoint point b11_2 ( {expr \$b11_2x} {expr \$b11_2y} 0 ) endpoint point b11_3 ( {expr \$b11_3x} {expr \$b11_3y} 0 ) endpoint point b11_4 ( {expr \$p12x} {expr \$p12y} 0 ) endpoint beziercurve bz11 controlpointlist (b11_1 b11_2 b11_3 b11_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b12_1 ( {expr \$p12x} {expr \$p12y} 0 ) endpoint point b12_2 ( {expr \$b12_2x} {expr \$b12_2y} 0 ) endpoint point b12_3 ( {expr \$b12_3x} {expr \$b12_3y} 0 ) endpoint point b12_4 ( {expr \$p13x} {expr \$p13y} 0 ) endpoint beziercurve bz12 controlpointlist (b12_1 b12_2 b12_3 b12_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b13_1 ( {expr \$p13x} {expr \$p13y} 0 ) endpoint point b13_2 ( {expr \$b13_2x} {expr \$b13_2y} 0 ) endpoint point b13_3 ( {expr \$b13_3x} {expr \$b13_3y} 0 ) endpoint point b13_4 ( {expr \$p14x} {expr \$p14y} 0 ) endpoint beziercurve bz13 controlpointlist (b13_1 b13_2 b13_3 b13_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b14_1 ( {expr \$p14x} {expr \$p14y} 0 ) endpoint point b14_2 ( {expr \$b14_2x} {expr \$b14_2y} 0 ) endpoint point b14_3 ( {expr \$b14_3x} {expr \$b14_3y} 0 ) endpoint point b14_4 ( {expr \$p15x} {expr \$p15y} 0 ) endpoint beziercurve bz14 controlpointlist (b14_1 b14_2 b14_3 b14_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve point b15_1 ( {expr \$p15x} {expr \$p15y} 0 ) endpoint point b15_2 ( {expr \$b15_2x} {expr \$b15_2y} 0 ) endpoint point b15_3 ( {expr \$b15_3x} {expr \$b15_3y} 0 ) endpoint point b15_4 ( {expr \$p1x} {expr \$p1y} 0 ) endpoint beziercurve bz15 controlpointlist (b15_1 b15_2 b15_3 b15_4) slices {expr \$bez_slices} drawcontrols {expr \$bez_drawc} endbeziercurve #################### # World #################### group gWorld instance points_to_interpolate endinstance instance bz1 endinstance instance bz2 endinstance instance bz3 endinstance instance bz4 endinstance instance bz5 endinstance instance bz6 endinstance instance bz7 endinstance instance bz8 endinstance instance bz9 endinstance instance bz10 endinstance instance bz11 endinstance instance bz12 endinstance instance bz13 endinstance instance bz14 endinstance instance bz15 endinstance endgroup group gScene instance gWorld endinstance endgroup #################### # CAMERA #################### camera cam projection SLF_PARALLEL frustum ( -9 -6 -100) (9 6 100) endcamera group gCamera instance cam id instCam translate ( 0.0 0.0 1.0 ) (* lookat eye ( 0.0 0.0 1.0 ) target ( 0.0 0.0 0.0 ) up ( 0.0 1.0 0.0 ) endlookat *) endinstance endgroup #################### # LIGHT #################### light lite type SLF_DIRECTIONAL # type SLF_POINT endlight group gLight instance lite id instLite # translate ( 0.0 1.0 1.0 ) lookat eye ( 1.0 1.0 1.0 ) target ( 0.0 0.0 0.0 ) up ( 0.0 1.0 0.0 ) endlookat endinstance endgroup light lite2 type SLF_AMBIENT color (0.5 0.5 0.5) endlight group gLight2 instance lite2 id instLite2 endinstance endgroup #################### # RENDER #################### window WINDOW origin (0.15 0.1) size (0.75 0.5) endwindow viewport VIEWPORT WINDOW origin ( 0.0 0.0 ) size ( 1.0 1.0 ) endviewport render VIEWPORT gCamera.instCam.cam gScene light gLight.instLite.lite light gLight2.instLite2.lite2 endrender