'''
//////////////////////////////////////////////////////////////////////////////////////////////////
/// Procedurally generated Nebula                                                              ///
///                                                                                            ///
/// To generate the Nebula I use a so called strange attractor to get the coordinates          ///
/// of a given number of particles in the Nebula.                                              ///
/// The coordinates are then used for vertices that represent points (particles of the Nebula) ///
///                                                                                            ///
///  by Legion of Dynamic Discord                                                              ///
//////////////////////////////////////////////////////////////////////////////////////////////////

'''

import direct.directbase.DirectStart
import math, sys, random
from pandac.PandaModules import *
from direct.showbase import DirectObject


class nebula:
    def __init__(self):
        
        #setting up everything to create our own geometry later
        format=GeomVertexFormat.getV3c4()
        vdata=GeomVertexData('points', format, Geom.UHDynamic)

        vertex=GeomVertexWriter(vdata, 'vertex')
        color = GeomVertexWriter(vdata, 'color')
        
        #create the starting conditions for our nebula... p0 through p3 control the behaviour of our strange attractor
        self.pos = Vec3(0,0,0)
        p0 = 1.4
        p1 = 2.3
        p2 = 0.77
        p3 = 1.58
        r = 1
        g = 1
        b = 1
        x = 1
        y = 1
        z = 1
        print "init nebula"
        
        #create the node that'll later hold the nebula
        self.nebulaNode = render.attachNewNode('Nebula')
        self.nebulaNode.setTransparency(TransparencyAttrib.MAlpha)
        
        #the number of particles will greatly affect the performance and look of the nebula
        particles = 100000
        
        for i in xrange(0,particles):
            
            #generate particle coordinates with a strange attractor 
            dx = math.sin(p0*y) - z * math.cos(p1*x)
            dy = z * math.sin(p2*x) - math.cos(p3*y)
            dz = math.sin(p2*y) - x * math.cos(p0*z)
            x = dx
            y = dy
            z = dz
            
            #set color values (written here, so one could change the rgba values for each particle seperately)
            r = 1
            g = 1
            b = 1
            alpha = 0.08
            
            #generate vertex and color data
            vertex.addData3f(x*120, y*120, z*120)
            color.addData4f(r,g,b,alpha)
        
        #create geom primitive and assign vertex data to it    
        prim = GeomPoints(Geom.UHStatic)
        prim.addConsecutiveVertices(0, particles)
        prim.closePrimitive()
        
        #create geom and geomNode
        geom = Geom(vdata)
        geom.addPrimitive(prim)

        node = GeomNode('gnode')
        node.addGeom(geom)
        
        #bring the new geometry into the render path (parent it to our nebulaNode)
        nodePath = self.nebulaNode.attachNewNode(node)
        nodePath.setRenderModeThickness(2)
        
        #shut off depth testing for performance
        nodePath.setBin("unsorted", 1)
        nodePath.setDepthWrite(0)
        

if __name__ == "__main__":
    #set some display properties
    base.setFrameRateMeter(True)
    base.setBackgroundColor(0,0,0)
    distance = 13000.0
    lens = base.cam.node().getLens()
    lens.setFar(distance)
    base.cam.node().setLens(lens)
    neb = nebula()
    base.cam.setPos(0,-700,0)
run()
