#!BPY

"""
Name: 'VR Object'
Blender: 236
Group: 'Animation'
Tooltip: 'Create Cameras and FrameChanged Script for VR Object's'
"""

__author__ = "Mitch Hughes"
__url__ = ("blender", "Author's homepage, http://blender.formworks.co.nz")
__version__ = "0.2"

__bpydoc__ = """\
"VR Object" creates cameras and a FrameChanged script to animate
them to produce the frames required to make a VR Object.

Usage:

Add the script to your blender/scripts directory
Run this script from the Scripts window, Scripts->Animation->VR Object
Choose the number of Columns, start and end angles.
Choose the number of rows, start and end angles.
Click Generate to build the rig, you can then play the animation
viewing through the active camera to make sure your object stays
in frame, you can easily move the rig by moving the Empty, which all the
cameras are parented to, and scaling the empty has the effect of zooming
in/out and effects all the cameras.
"""

# $Id: vr_object.py,v 0.1 2005/04/24 18:35:10$
#
# --------------------------------------------------------------------------
# VR Object by Mitch Hughes (AKA lobo_nz)
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
import Blender
from Blender import Scene, Object, Scene, Camera, Window, Text, Draw, BGL
from Blender.Window import *
import math

g_identifier = Draw.Create("TEST")

g_x_steps = Draw.Create(36) # Frames in horizontal movement
g_y_steps = Draw.Create(10) # Frames in vertical movement

g_x_start = Draw.Create(0) #starting horizonatal angle
g_x_total = Draw.Create(360) #ending horizonatal angle

#NOTE: The y_start, y_end are angles in degrees
#      The range is -90 to 90, 0 being level
#      You should probably not use 90 or -90 as these
#      angles generate Circular patch with a radius of 0
#      so your mileage may vary
g_y_start = Draw.Create(-45) #starting horizonatal angle
g_y_end = Draw.Create(45) #ending horizonatal angle

g_EmptyName = Draw.Create('VR_TARGET')
g_CameraBaseName = Draw.Create('VR_CAM')
g_vr_scriptname = Draw.Create('VR_ROTATOR')

g_sphere_radius = Draw.Create(3) #path radius

g_scene = Scene.getCurrent()

# Events
EVENT_NOEVENT=1
EVENT_GENERATE=2
EVENT_EXIT=100




def setup_render():
    global g_x_steps, g_y_steps, g_scene
    #set the end frame of the animation
    context = g_scene.getRenderingContext()
    context.startFrame(1)
    context.endFrame(g_x_steps.val*g_y_steps.val)

def rad2deg(v):
    return round(v*180.0/math.pi,4)
def deg2rad(v):
    return (v*math.pi)/180.0;

#this is unused
def draw_circle(curve,loc, radius):
    
    curve.appendNurb (   [ radius, -radius, loc[2], circ_w])
    curve.appendPoint(0, [ radius,     0.0, loc[2], 1.0])
    curve.appendPoint(0, [ radius,  radius, loc[2], circ_w])
    curve.appendPoint(0, [ 0.0,     radius, loc[2], 1])
    curve.appendPoint(0, [-radius,  radius, loc[2], circ_w])
    curve.appendPoint(0, [-radius,     0.0, loc[2], 1])
    curve.appendPoint(0, [-radius, -radius, loc[2], circ_w])
    curve.appendPoint(0, [ 0.0,    -radius, loc[2], 1])
    
    curve.appendPoint(0, [ radius, -radius, loc[2], circ_w])
    curve.appendPoint(0, [ radius,     0.0, loc[2], 1.0])
    curve.appendPoint(0, [ radius,  radius, loc[2], circ_w])

    curve.setPathLen(x_steps)

def pos(number):
    if number < 0:
        return number * -1
    else:
        return number

def get_height(angle):
    global g_sphere_radius
    height = math.sin(deg2rad(angle)) * g_sphere_radius.val
    return height


def get_radius(height):
    global g_sphere_radius
    #calculate radius at height
    return  math.sqrt((g_sphere_radius.val*g_sphere_radius.val) - (pos(height)*pos(height)))

    
def vr_generate():
    global g_x_steps, g_y_steps, g_x_start, g_x_total, g_y_start, g_y_end
    global g_EmptyName, g_CameraBaseName, g_vr_scriptname, g_sphere_radius, g_scene
    
    y_step = (pos(g_y_start.val) + pos(g_y_end.val))/g_y_steps.val
    
    if g_y_end.val < g_y_start.val:
        y_step = y_step * -1
    
    current_y_angle = g_y_start.val
    current_y_step = 0
    heights = []
    curve_objects = []
    radii = []
    cameras = []
    camera_objects = []
    
    #calculate heights and radii of camera paths
    while current_y_step < g_y_steps.val:
        #print "current_y_angle = ", current_y_angle
        #print "current_y_step = ", current_y_step
        
        heights.append(get_height(current_y_angle))
        radii.append(get_radius(heights[current_y_step]))
        
        current_y_angle = current_y_angle + y_step
        current_y_step = current_y_step + 1
    
    #Make Empty
    empty = Object.New('Empty',g_EmptyName.val)  # make empty object
    g_scene.link(empty)                   
    
    current_y_step = 0
    #Make Cameras and Target them to the empty
    
    while current_y_step < g_y_steps.val:
        camera_objects.append(Object.New('Camera', g_CameraBaseName.val)) # make camera object
        cameras.append(Camera.New ('persp'))  # make perspective camera data object    
        camera_objects[current_y_step].link(cameras[current_y_step]) # link camera data with the object
        g_scene.link (camera_objects[current_y_step]) # link the object into the scene
        
        camera_objects[current_y_step].makeTrack(empty,0)
        camera_objects[current_y_step].setLocation(0,-radii[current_y_step],heights[current_y_step])
        Window.Redraw()
    
        camera_objects[current_y_step].clearTrack(1,0)
        
        current_y_step = current_y_step + 1
        
    #parent cameras to empty
    empty.makeParent(camera_objects,0,0)\
    #set the empty to the starting angle
    empty.setEuler([0,0,deg2rad(g_x_start.val)])
    
    #write a rotator script for the empty
    txt = Text.New(g_vr_scriptname.val)
    txt.write("import Blender\nfrom Blender import Scene\n")
    txt.write("scene = Scene.getCurrent()\n")
    
    txt.write("cameras = [")
    for camera_object in camera_objects:
        txt.write("'"+camera_object.name+"', ")
    txt.write("]\n")
    txt.write("frame=Blender.Get('curframe')\n")
    
    txt.write("step = "+str(deg2rad(g_x_total.val/(g_x_steps.val-1)))+"\n")
    txt.write("steps = "+str(g_x_steps.val)+"\n")
    
    txt.write("x_start = "+str(deg2rad(g_x_start.val))+"\n")
    txt.write("x_total = "+str(deg2rad(g_x_total.val))+"\n")
    
    txt.write("empty = Blender.Object.Get('"+str(g_EmptyName.val)+"') #get the vr empty\n")
    txt.write("euler = empty.getEuler()\n")
    
    txt.write("revolution = (frame-1)/steps\n\n")
    txt.write("frames_this_rev = (frame-1)%steps\n\n")
    
    txt.write("euler[2] = (frames_this_rev * step) + x_start\n")
    txt.write("empty.setEuler(euler)\n")
    txt.write("scene.setCurrentCamera(Blender.Object.Get(cameras[revolution]))\n")
    
    #Link the script to the scene
    g_scene.addScriptLink(g_vr_scriptname.val,'FrameChanged')
    setup_render()
    
def DRAWX(val):
    global g_drawx
    if val >= 0:
        g_drawx=g_drawx-val
        return g_drawx
    else:
        g_drawx = val*-1
        return g_drawx
######################################################
# GUI Loader
######################################################
def draw_gui():
    global EVENT_NOEVENT,EVENT_GENERATE,EVENT_EXIT
    global g_x_steps, g_y_steps, g_x_start, g_x_total, g_y_start, g_y_end
    global g_EmptyName, g_CameraBaseName, g_vr_scriptname, g_sphere_radius, g_scene
    
    DRAWX(-280)
    
    ########## Titles
    BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
    BGL.glRasterPos2d(8, DRAWX(20))
    Draw.Text("            VR Object Camera Generator         ")
    BGL.glRasterPos2d(8, DRAWX(20))
    Draw.Text("1) Specify Rows, Columns and Angle Limits")
    #BGL.glRasterPos2d(8, DRAWX(0))
    
    #Slider(name, event, x, y, width, height, initial, min, max, realtime=1, tooltip=None)
    g_x_steps = Draw.Slider("Columns: ", EVENT_NOEVENT, 10, DRAWX(30), 250, 18, g_x_steps.val, 1, 60, 1, "Number of frames per revolution")
    g_x_start = Draw.Slider("Angle Start: ", EVENT_NOEVENT, 10, DRAWX(20), 250, 18, g_x_start.val, -180, 180, 1, "Start angle for revolution")
    g_x_total = Draw.Slider("Angle Total: ", EVENT_NOEVENT, 10, DRAWX(20), 250, 18,   g_x_total.val, -360, 360, 1, "Total angle of revolution")
    
    g_y_steps = Draw.Slider("Rows: ", EVENT_NOEVENT, 10, DRAWX(30), 250, 18, g_y_steps.val, 1, 60, 1, "Number of frames in Vertical movement")
    g_y_start = Draw.Slider("Angle Start: ", EVENT_NOEVENT, 10, DRAWX(20), 250, 18, g_y_start.val, -89, 89, 1, "Start angle for Vertical movement (0 is horizontal)")
    g_y_end   = Draw.Slider("Angle End: ", EVENT_NOEVENT, 10, DRAWX(20), 250, 18,   g_y_end.val, -89, 89, 1, "End angle for Vertical movement (0 is horizontal)")

    g_sphere_radius   = Draw.Slider("Sphere Radius: ", EVENT_NOEVENT, 10, DRAWX(40), 250, 18,   g_sphere_radius.val, 1, 10, 1, "Radius of the sphere the cameras sit on")
    
    BGL.glRasterPos2d(8, DRAWX(25))
    Draw.Text("2) Press Generate")

    ######### Draw and Exit Buttons
    Draw.Button("Generate",EVENT_GENERATE , 10, DRAWX(30), 80, 18)
    Draw.Button("Exit",EVENT_EXIT , 180, DRAWX(0), 80, 18)

def event(evt, val):
    if (evt == Draw.QKEY and not val):
        Draw.Exit()

def bevent(evt):
    global EVENT_NOEVENT,EVENT_GENERATE,EVENT_EXIT

    ######### Manages GUI events
    if (evt==EVENT_EXIT):
        Draw.Exit()
    elif (evt==EVENT_GENERATE):
        vr_generate()
        Draw.Exit()

Draw.Register(draw_gui, event, bevent)


