Come: Utilizzare POV-Ray con Blender (Pagina dei Sviluppatori)

From POV-Wiki
Jump to navigation Jump to search


🌎    🇬🇧 / 🇫🇷 / 🇮🇹 /


▼ Developer's page

POVExporterLogo.png
Go To User's page ►



Il componente aggiuntivo Blender per POV-Ray e viceversa (noto anche come "POV@Ble") è iniziato come un singolo file Python. Per rispettare i criteri per le estensioni disponibili con Blender, questo componente aggiuntivo doveva:

  • fornire alcune informazioni generali sul componente aggiuntivo: nome e versione,
  • definire il codice per eseguire azioni, principalmente tramite "operatori" o "funzioni"
  • e assicurarsi che siano iscritti nel registro affinché possano essere utilizzati.


bpy

Blender dispone di un interprete Python integrato che è incaricato di avviare Blender e resta attivo mentre Blender è in corso di esecuzione. L'interprete esegue script per progettare l'utente dell'interfaccia ed è anche utilizzato per alcuni strumenti interni di Blender. L'interprete integrato di Blender fornisce un ambiente tipico Python, quindi il codice didattico sulla forma di scrittura degli script Python può essere eseguito anche con l'interprete di Blender. Frullatore fornitomoduli Python, come bpy e mathutils, per la console integrata affinché possano essere importati in uno script e accedere ai dati, alle classi e alle funzioni di Blender. Gli script che trattano i dati di Blender devono essere l'oggetto di unimport di moduli per funzionare. Quindi, tra tutti i moduli, se non ce n'è un altro, bpy è generalmente sempre importato negli script di Blender. (eccetto nella console interattiva dove questa importazione viene eseguita automaticamente per maggiore comodità)

La console interattiva di Blender


metadati del componente aggiuntivo

bl_info = {
    "name": "Persistence of Vision",
    "author": "Campbell Barton, "
    "Maurice Raybaud, "
    "Leonid Desyatkov, "
    "Bastien Montagne, "
    "Constantin Rahn, "
    "Silvio Falcinelli",
    "version": (0, 1, 1),
    "blender": (2, 81, 0),
    "location": "Render Properties > Render Engine > Persistence of Vision",
    "description": "Persistence of Vision integration for blender",
    "doc_url": "{BLENDER_MANUAL_URL}/addons/render/povray.html",
    "category": "Render",
    "warning": "Under active development, seeking co-maintainer(s)",
}


Prima di Blender 4.2, le informazioni generali sui componenti aggiuntivi erano definite in un dizionario denominato

bl_info

Ciascuna chiave in questo dizionario forniva a Blender informazioni specifiche sull'add-on, sebbene non tutte fossero ugualmente importanti. La maggior parte delle informazioni sono state utilizzate nella finestra di dialogo delle preferenze dell'utente e hanno aiutato l'utente a trovare e selezionare un componente aggiuntivo.

name

Una sorta di nome in codice per l'add-on, con cui viene indicato nei sorgenti di Blender... (il nome dell'interfaccia utente potrebbe mostrare qualcos'altro)

author

Se contribuisci, ti verrà accreditato qui!

version

Numero di versione del software. Qualsiasi schema di numerazione è valido, purché sia ​​una tupla di tre numeri interi. È preferibile spostare solo l'ultima posizione, ma in seguito potremmo optare per uno schema più strutturato.

blender

La versione minima di Blender richiesta da questo componente aggiuntivo. Un'altra tupla di tre numeri interi. Anche se ti aspetti che le cose funzionino con le versioni più vecchie e quelle più recenti, potrebbe essere una buona idea elencare la versione più vecchia con cui hai effettivamente testato il tuo componente aggiuntivo! È importante sviluppare con qualcosa di abbastanza stabile e poi testare con l'ultima versione appena prima di impegnarsi, ma generalmente non aggiornare il numero di versione compatibile, solo quando qualcosa si rompe a quel punto.

category

La categoria nelle Preferenze utente in cui è raggruppato il nostro componente aggiuntivo. Sfrutta motori compatibili con POV che sono motori di rendering, quindi aveva senso aggiungerlo storicamente alla categoria Render. Sfortunatamente, questo non riflette la natura versatile che ha finito per sviluppare (come la spedizione a un importatore, l'evidenziazione della sintassi dell'editor di testo, ecc...). Se sono possibili più categorie, forse dovremmo aggiungerle.

location

Dove trovare il componente aggiuntivo una volta attivato. Può trattarsi di un riferimento a un pannello specifico o a un caso particolare, una descrizione della sua posizione in un menu.

description

Una descrizione concisa di ciò che fa il componente aggiuntivo.

warning

Se non è una stringa vuota, il componente aggiuntivo verrà visualizzato con un segno di avviso nelle preferenze dell'utente. Puoi usarlo per contrassegnare un componente aggiuntivo come sperimentale, ad esempio.

doc_url

La documentazione pubblica online è un derivato del lavoro svolto qui. Questo sarà un elemento cliccabile nelle preferenze dell'utente.

tracker_url

Dato che il componente aggiuntivo POV è una parte incorporata di Blender, viene fornito con una propria voce di tracciamento dei bug e questa chiave fornirà un puntatore ad esso.

Commenti

I commenti a riga singola iniziano con un carattere cancelletto (#) immediatamente seguito da uno spazio vuoto e il commento inizia con una lettera maiuscola ma non termina con un punto. Possono essere aggiunti dopo una riga di codice sufficientemente breve (lasciare uno spazio vuoto prima del #).


Una delle linee guida per la formattazione del testo/stile di codifica da utilizzare è PEP8 quindi, ad esempio, dovresti fare attenzione a creare righe abbastanza brevi in ​​tutto il testo file di script con meno di 79 caratteri e meno di 72 per le docstring (vedi sotto) o i commenti.

Periodicamente la Blender Foundation esegue controlli per questa conformità pep8 sugli script di Blender. La lunghezza della riga tollerata è arrivata a raggiungere i 100 caratteri.

Se devi davvero superare questo limite, ad esempio per promemoria temporanei di lavori in corso, aggiungi

# nopep8

alla fine della riga pertinente

Altre regole includono:

  • Camel caps per i nomi delle classi: MyClass
  • tutti i nomi dei moduli separati da caratteri di sottolineatura minuscoli: mio_modulo
  • 4 rientri di spazio (nessuna tabulazione)
  • spazi attorno agli operatori matematici: 1 + 1, non 1+1
  • Utilizzare solo importazioni esplicite (non eseguire mai import *)
  • Non utilizzare una sola riga per un test e l'istruzione che dipende da esso: if val : body, invece, separare i blocchi di istruzioni dalle condizioni da cui dipendono di almeno 2 righe.

Mentre i commenti su più righe possono iniziare con virgolette triple. È convenzione che vengano usate tre virgolette doppie per docstrings, un tipo specifico di commento che non fa nulla nel programma ma che appare comunque quando la console viene utilizzata per ottenere aiuto su una funzione.

"""Questa è una spiegazione generale"""

Oltre ad essere una buona pratica inserire una stringa di documento all'inizio di ogni classe per descriverla e anche all'inizio di ogni file per spiegarne l'utilizzo relativo, ciò consente anche alle chiamate da riga di comando di visualizzare questo documento su richiesta. Questa è una delle principali differenze tra i commenti docstrings e inline (solo su parte di una riga).

Funzioni

Le funzioni sono definite con def, seguita dal nome della funzione e da una coppia di parentesi. Quando si fa riferimento alla funzione per spostarla e passarla da un file all'altro, non ha più bisogno delle parentesi, ma quasi ogni altra volta queste seguono il nome della funzione e tra di esse si possono facoltativamente specificare alcuni attributi.

Classi

Le Classi non solo ti consentono di creare definizioni riutilizzabili. Ma questo è l'unico modo per configurare gli elementi dell'interfaccia utente in Blender. La buona notizia è che ciò significa che gli sviluppatori di componenti aggiuntivi hanno effettivamente meno lavoro da fare. L'implementazione hardcoded si occupa di determinare quando e quali parti aggiornare automaticamente per tutti gli elementi dell'interfaccia utente e l'ereditarietà delle classi tra cui scegliere (l'API) garantisce che si possano gestire le situazioni in modo ottimizzato. Alcune funzioni legacy come draw() devono essere sovrascritte dallo sviluppatore del componente aggiuntivo che le riscrive in base alle proprie esigenze.

Per creare nuove classi, utilizzare il consueto schema di definizione delle classi come mostrato nei modelli Python nell'editor di testo interno di Blender... Oppure copiarne e incollarne uno altrove nello script.

Nomenclatura delle classi

Rispettare il più possibile lo stesso modello di denominazione richiesto dalla Blender Foundation: OT significa che è un operatore, MT un menu, PT un pannello: Il nome della classe deve essere composto come segue: NOMEDELLADDON_PT_pannello_principale NOMEDELLADDON - In lettere maiuscole digitiamo il nome del componente aggiuntivo. È possibile utilizzare un carattere di sottolineatura se il nome contiene più di una parola.

_PT_ - viene poi separata da due lettere che designano il tipo di classe (il cui tipo è ereditato).

pannello_principale - utilizzando le lettere minuscole possiamo digitare il nome dell'operatore/pannello/intestazione ecc.

Altri tipi di classi:

   HT – Header
   MT – Menu
   OT – Operator
   PT – Panel
   UL – UI list

Quindi, nel nostro caso il nome del componente aggiuntivo è POV, ecco alcuni esempi di nomi di classi validi e relativi identificatori:

class POV_MATERIAL_PT_replacement_field(bpy.types.Panel):
    bl_idname = 'pov_PT_replacement_field_panel'

	
class POV_OT_add_basic(bpy.types.Operator):
    bl_idname = 'pov.addbasicitem'

	
class POV_VIEW_MT_Basic_Items(bpy.types.Menu):
    bl_idname = 'pov_MT_basic_items_tools'

Notate come l'ID dell'operatore è diverso? Non è necessario aggiungere "OT" ma piuttosto un punto. Inoltre non è necessario aggiungere "_operator" alla fine.


L'API Python di Blender consente l'integrazione di:


   bpy.types.Panel
   bpy.types.Menu
   bpy.types.Operator
   bpy.types.PropertyGroup
   bpy.types.KeyingSet
   bpy.types.RenderEngine

Questo è intenzionalmente limitato. Attualmente, per funzionalità più avanzate come i modificatori di mesh, i tipi di oggetto o i nodi shader, dovrebbe essere utilizzato C/C++.

Per l'integrazione con Python, Blender definisce metodi comuni a tutti i tipi. Funziona creando una sottoclasse Python di una classe Blender che contiene variabili e funzioni specificate dalla classe genitore che sono predefinite per interfacciarsi con Blender.

Nota che stiamo creando una sottoclasse di un membro di bpy.types, questo è comune a tutte le classi che possono essere integrate in Blender e utilizzate, quindi sappiamo se si tratta di un operatore e non di un pannello quando entri nel registro.


Entrambe le proprietà della classe iniziano con il prefisso bl_. Questa è una convenzione usata per distinguere le proprietà di Blender da quelle che aggiungi tu stesso.

Operatori

La maggior parte dei componenti aggiuntivi definisce nuovi operatori, si tratta di classi che implementano funzionalità specifiche. La definizione effettiva dell'operatore prende la forma di una classe derivata da bpy.types.Operator

import bpy
class MyTool(bpy.types.Operator):
    """Fai cose su un oggetto"""    
    bl_idname = "object.fai_cose_su_oggetto"
    bl_label = "Fai cose su un oggetto"
    bl_options = {'REGISTER', 'UNDO'}

La docstring all'inizio della definizione della classe verrà utilizzata come tooltip ovunque questo operatore sia disponibile, ad esempio in un menu, mentre bl_label definisce l'etichetta effettiva utilizzata nella voce di menu stessa. Qui li abbiamo mantenuti entrambi uguali. Gli operatori faranno parte dei dati di Blender e saranno memorizzati nel modulo bpy.ops. Questo bl_idname garantirà che l'input per questo operatore verrà chiamato bpy.ops.object.move_object. Gli operatori vengono normalmente registrati in modo da renderli utilizzabili e questa è infatti l'impostazione predefinita di bl_options. Tuttavia, se vogliamo che il componente aggiuntivo appaia anche nella cronologia in modo che possa essere annullato o ripetuto, dobbiamo aggiungere UNDO all'insieme di flag assegnati a bl_options, come qui.


Gli operatori hanno limitazioni che possono rendere noiosa la loro scrittura.

I limiti principali sono...

Non è possibile trasferire dati come oggetti, mesh o materiali su cui operare (gli operatori utilizzano invece il contesto)

Il valore restituito dalla chiamata di un operatore è successo (indipendentemente dal fatto che sia stato completato o annullato), in alcuni casi avrebbe più senso per l'API restituire il risultato dell'operazione.

La funzione poll() per richiestare gli operatori potrebbe non riuscire quando una funzione API lancia un'eccezione fornendo i dettagli del motivo esatto.

La funzione execute()

Una classe operatore può avere un numero qualsiasi di funzioni membro ma per essere utile normalmente sovrascrive la funzione execute() :

def execute(self, context):
    context.active_object.rotation.z += 33
    return {'FINISHED'}

La funzione execute() viene passata un riferimento a a a contesto oggetto. Questo oggetto di contesto contiene tra l'altro un attributo active_object che punta a l'oggetto attivo di Blender. Ogni oggetto in Blender ha un attributo di rotazione che è un vettore con componenti x, y e z. Cambiare la rotazione di un oggetto è semplice come cambiare uno di questi componenti, che è esattamente ciò che facciamo nella riga 2.

La funzione execute() segnala l'esecuzione riuscita restituendo un set di flag, in questo caso un set costituito solo dalla stringa di caratteri "FINISHED".

Creazione e aggiunta di un menu al registro

Quando abilitiamo la casella di controllo Enable add-on nelle preferenze, Blender cercherà una funzione register() e la eseguirà. Allo stesso modo, quando si disattiva un componente aggiuntivo, viene richiamata la funzioneunregister(). Usiamoli quindi sia per registrare il nostro operatore con Blender sia per inserire una voce di menu che faccia riferimento al nostro operatore. è inoltre importante annullare l'iscrizione e disattivarla affinché il nostro componente aggiuntivo non venga percepito come invasivo o distruttivo di una configurazione precedente senza possibile ritorno.

Per creare un menu dobbiamo fare due cose:

  • Crea una funzione che produrrà una voce di menu
  • E aggiungi questa funzione al menu appropriato.

Al giorno d'oggi quasi tutto in Blender è disponibile come oggetto Python e i menu non fanno eccezione. Vogliamo aggiungere la nostra voce al menu Oggetto nella vista 3D, quindi chiamiamo

bpy.types.VIEW3D_MT_object.append()

e passargli un riferimento alla funzione che definiamo poco più sotto. Come facciamo a sapere il nome di questo oggetto di menu? Se hai attivato File⇒ User preferences ⇒ Interface ⇒ Python Tooltips Il nome del menu verrà visualizzato in una descrizione comando quando passi il mouse su un menu. Dall'immagine sopra possiamo vedere che possiamo usare bpy.types.VIEW3D_MT_object.append() per aggiungere qualcosa al menu Oggetto perché VIEW3D_MT_object viene visualizzato nel testo della bolla che appare quando si passa con il mouse su questo menu.

BlightBulb.png
La funzione menu_func() non implementa un'azione stessa (questo è il ruolo di un operatore)
Ma quando viene chiamato, menu_func() aggiungerà un elemento UI che punta all'oggetto passatogli nel parametro self. Questo elemento dell'interfaccia potrà essere modificato dall'utente e, a sua volta, interagirà con l'operatore.


Qui aggiungeremo semplicemente un input dell'operatore (ovvero un elemento che eseguirà il nostro operatore quando viene cliccato).

L'argomento self passato a menu_func() si riferisce al menu.

Questo menu ha un attributo layout con una funzione operator() alla quale passiamo il nome del nostro operatore. Ciò garantirà che ogni volta che un utente passa con il mouse sul menu Oggetto, il nostro operatore verrà visualizzato nell'elenco delle opzioni.

l nome del nostro nuovo operatore MyTool è nel suo attributo bl_idname, motivo per cui passiamo MyTool.bl_idname.

Il nome della voce e il suo tooltip sono determinati guardando bl_label e docstring definiti nella nostra classe MyTool e l'icona utilizzata nel menu è determinata passando un comando facoltativo parametro icon alla funzione operator().

Definire un menu non è di per sé sufficiente a renderlo utilizzabile. Affinché l'utente possa trovarlo e utilizzarlo, dobbiamo aggiungere il menu al suo genitore dalla funzione register() alla fine del suo file python.

def menu_func(self, context):
    self.layout.operator(MyTool.bl_idname, icon='MESH_CUBE')
def register():    
    bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
    bpy.types.VIEW3D_MT_object.remove(menu_func)

Gli operatori devono essere registrati allo stesso modo, quindi aggiungere un operatore registrato a un menu richiede da due a tre azioni separate:

  • Registrati operatore;
  • quindi aggiungilo a un menu esistente;
  • Oppure salvare prima il menu appena creato a cui dovrebbe appartenere e solo dopo aggiungervi l'operatore.

Solo dopo tutto ciò il nuovo operatore apparirà sull'interfaccia utente, almeno, se non altro, premendo F3 nella finestra di visualizzazione 3D e digitando l'etichetta dell'operatore

Tutto questo potrebbe sembrare un po' più complicato del necessario ma permette ad esempio di visualizzare molte altre cose oltre alle semplici voci cliccabili in un menu, ad esempio di raggruppare più operatori nella stessa casella, ad esempio se fanno parte di modalità disponibili in lo stesso sottotema, ecc.

BlightBulb.png
I comandi di unregister() devono apparire in ordine inverso rispetto alle righe register() corrispondenti
Ciò garantisce che tu non provi ad annullare la registrazione, ad esempio, di un menu che è stato aggiunto come figlio di una classe di elementi dell'interfaccia utente che potresti provare a annullare la registrazione prima di quel figlio, annullando così la registrazione di qualcosa che già non è più presente... Il che genererebbe un errore e la mancata attivazione/disattivazione del componente aggiuntivo.


Ottimizzazione

Mantieni la notazione "puntata" minima

Come in molti linguaggi orientati agli oggetti, Python consente la visualizzazione di una struttura gerarchica con i suoi livelli separati da punti (pensa a ciascun punto come l'apertura della bambola russa più grande a sinistra che porta quelli più piccoli alla sua destra).

BlightBulb.png
Sostituisci le sue ricerche inutilmente ripetute con alias
L'operatore punto è un modo semplice per acquisire classe, dati, metodo, qualunque oggetto python incluso in una gerarchia più ampia ... Ma tieni presente che è costoso, specialmente se usato in un ciclo.

Quando si chiama

bpy.data.objects["my_obj"]

Ogni punto si tuffa di un livello più in profondità e l'intera gerarchia viene raggiunta. Devi sempre farlo almeno una volta per raggiungere l'elemento desiderato, quindi va bene usare quella sintassi quando il riferimento viene fatto solo una volta, ma soprattutto, preferisci sempre evitare l'intera ricerca e piuttosto dargli un alias come

obj = bpy.data.objects["my_obj"]

Quindi puoi ancora usarlo, gestendo attributi occasionali come l'esempio seguente:

obj.location.z += 1

e tutti gli altri attributi disponibili, per le operazioni di lettura o scrittura. Definire molti di questi alias necessari prima di entrare in un ciclo piuttosto che all'interno di esso. Immagina se il tuo GPS ti dicesse sempre le indicazioni precedenti prese dal punto di partenza fino a quella successiva ad ogni turno!

Usare try/except con parsimonia

L'istruzione try è utile per risparmiare tempo per scrivere il codice di controllo degli errori.

Tuttavia, try è significativamente più lento di un if perchè un'eccezione deve essere impostata ogni volta, quindi evita di utilizzare try nelle aree del codice che vengono eseguite in un ciclo e vengono eseguite molte volte.

Ci sono casi in cui l'uso di try è più veloce che rilevare esplicitamente tramite un test (if o altro) la condizione complessa che potrebbe potenzialmente generare un errore, quindi vale la pena sperimentare.


Un buon modo per rimuovere alcuni dei tuoi try ... except quando l'errore trovato risulta essere un AttributeError è sostituirli con un pre-test con la funzione ' 'hasattr():

for member in dir(properties_freestyle):
    subclass = getattr(properties_freestyle, member)
    try:
        subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
    except:
        pass

potrebbe diventare

for member in dir(properties_freestyle):
   subclass = getattr(properties_freestyle, member)
   if hasattr(subclass, "COMPAT_ENGINES"):
       subclass.COMPAT_ENGINES.add('POVRAY_RENDER')

Se devi usare try:... except:... non lascia il tipo di eccezione non specificato, questo non fornisce assolutamente alcun feedback o leggibilità del codice. Se invece non hai idea di cosa potrebbe essere, sostituiscilo con qualcosa che dia maggiori informazioni durante la fase di debug:

(BaseException è al top della gerarchia delle eccezioni)

def your_function(b, c):
    try:
        a = b + c
        return True
    except BaseException as e:
        print(e.__doc__)
        print('An exception occurred: {}'.format(e))
        return False

Separare i file python (*.py)

Lo script è stato successivamente suddiviso in unpackage (diversi file in una cartella) utilizzando il minor numero di file possibile per rendere il flusso di dati più semplice da comprendere. Inizialmente c'erano 3 file principali:

  • __init__.py
  • ui.py
  • render.py

Tuttavia, alcuni file superavano le 10.000 righe, che per un componente aggiuntivo di questo tipo erano ancora troppo grandi. Attualmente è quindi tollerato che il file più grande rimanga al di sotto delle 2000 righe. È molto probabile che i file possano perdere peso, rimuovendo l'obsoleto supporto a 32 bit e modularizzando parte del codice in alcune funzioni più riutilizzabili. Ecco i file attuali che compongono il componente aggiuntivo e i rispettivi usi:

inizializzazione

__init__.py

Crea le preferenze utente del componente aggiuntivo POV, ordina all'utente di aggiornare il componente aggiuntivo all'ultima versione e carica tutti gli altri moduli. (Nota il doppio sottolinea il nome del file. Questo lo rende il primo file lanciato) la definizione dell'unità "principale" per un pacchetto; fa anche in modo che Python tratti la directory specifica come un pacchetto. È l'unità che verrà utilizzata quando si chiama import render_povray (e render_povray è una directory).

scenography_properties.py

Inizializzazione delle proprietà per tradurre le impostazioni della fotocamera/luce/ambiente di Blender nel linguaggio POV.

object_properties.py

Inizializzazione delle proprietà per tradurre le impostazioni dell'oggetto Blender nel linguaggio POV.

shading_properties.py

Inizializzazione delle proprietà per tradurre le impostazioni dei materiali di Blender nel linguaggio POV.

texturing_properties.py

Inizializzazione delle proprietà per tradurre i parametri di influenza delle texture dei materiali, dell'ambiente (mondo), ecc. da Blender al linguaggio POV.

render_properties.py

Inizializzazione delle proprietà per tradurre i parametri di rendering (da Blender ma soprattutto quelli più specificatamente nativi di POV).

scripting_properties.py

Inizializzazione di proprietà e campi per popolare le istruzioni direttamente nel linguaggio di descrizione della scena POV.

Interfaccia

Alcune note da tenere a mente durante la creazione di elementi dell'interfaccia utente:

I controlli che Blender fornisce per l'interfaccia utente sono relativamente semplici. Le dichiarazioni di interfaccia servono per creare facilmente un layout accettabile. Regola generale qui: se hai bisogno di più codice per il layout dell'interfaccia rispetto alle proprietà stesse, allora stai sbagliando.

Esempi di interfaccia:

   layout()

Il layout di base è un semplice layout Alto -> Basso.

   layout.prop()
   layout.prop()
   layout.row()

Usa row(), quando vuoi più di 1 proprietà in una riga.

   row = layout.row()
   row.prop()
   row.prop()
   layout.column()

Usa column(), quando vuoi che le tue proprietà siano in una colonna.

   col = layout.column()
   col.prop()
   col.prop()
   layout.split()

Questo può essere utilizzato per creare layout più complessi. Ad esempio puoi dividere il layout e creare due layout column() uno accanto all'altro. Non utilizzare la suddivisione quando desideri semplicemente due proprietà di seguito. Usa row() per quello.


   split = layout.split()
   col = split.column()
   col.prop()
   col.prop()
   col = split.column()
   col.prop()
   col.prop()

Nomi delle dichiarazioni:

Prova a utilizzare solo questi nomi di variabili per le dichiarazioni di layout:

row per un layout row()

col per un layout column()

split per un layout split()

flow per un layout column_flow()

sub per un layout sub() (una colonna all'interno di una colonna per esempio)

base_ui.py

Fornisce un workspace simile a Moray e definisce alcune funzioni di utilità. Carica tutti gli altri moduli relativi alla GUI

scenography_gui.py)

Visualizza le proprietà camera/luce/ambiente da scenography_properties.py affinché l'utente possa modificarle

object_gui.py :

Display properties from object_properties.py for user to change them


shading_gui.py

Display properties from shading_properties.py for user to change them


shading_nodes.py

Translate node trees to the pov file


texturing_gui.py

Display properties from texturing_properties.py for user to change them


render_gui.py

Display properties from render_properties.py for user to change them


scripting_gui.py

Display properties from scripting_properties.py for user to add his custom POV code

Render / import / edit

render.py

Translate geometry and UI properties (Blender and POV native) to the POV file


scenography.py

Translate cam/light/environment properties to corresponding pov features


primitives.py

Display some POV native primitives in 3D view for input and output


object_mesh_topology.py

Translate to POV the meshes geometries


object_curve_topology.py

Translate to POV the curve based geometries


object_particles.py

Translate to POV the particle based geometries


shading.py

Translate shading properties to declared textures at the top of a pov file


texturing.py

Translate blender texture influences into POV


df3_library.py

Render smoke to *.df3 files


scripting.py

Insert POV native scene description elements to exported POV file


update_files.py

Update new variables to values from older API. This file needs an update

Presets

Along these essential files also coexist a few additional libraries to help make Blender stand up to other Persistence of Vision compatible frontends such as povwin or QTPOV

Material (sss)

apple.py

chicken.py

cream.py

Ketchup.py

marble.py

potato.py

skim_milk.py

skin1.py

skin2.py

whole_milk.py

Radiosity

01_Debug.py

02_Fast.py

03_Normal.py

04_Two_Bounces.py

05_Final.py

06_Outdoor_Low_Quality.py

07_Outdoor_High_Quality.py

08_Outdoor(Sun)Light.py

09_Indoor_Low_Quality.py

10_Indoor_High_Quality.py

World

01_Clear_Blue_Sky.py

02_Partly_Hazy_Sky.py

03_Overcast_Sky.py

04_Cartoony_Sky.py

05_Under_Water.py

Light

01_(4800K)_Direct_Sun.py

02_(5400K)_High_Noon_Sun.py

03_(6000K)_Daylight_Window.py

04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py

05_(4000K)_100W_Metal_Halide.py

06_(3200K)_100W_Quartz_Halogen.py

07_(2850K)_100w_Tungsten.py

08_(2600K)_40w_Tungsten.py

09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py

10_(4300K)_40W_Vintage_Fluorescent_T12.py

11_(5000K)_18W_Standard_Fluorescent_T8.py

12_(4200K)_18W_Cool_White_Fluorescent_T8.py

13_(3000K)_18W_Warm_Fluorescent_T8.py

14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py

15_(3200K)_40W_Induction_Fluorescent.py

16_(2100K)_150W_High_Pressure_Sodium.py

17_(1700K)_135W_Low_Pressure_Sodium.py

18_(6800K)_175W_Mercury_Vapor.py

19_(5200K)_700W_Carbon_Arc.py

20_(6500K)_15W_LED_Spot.py

21_(2700K)_7W_OLED_Panel.py

22_(30000K)_40W_Black_Light_Fluorescent.py

23_(30000K)_40W_Black_Light_Bulb.py

24_(1850K)_Candle.py

templates

abyss.pov

biscuit.pov

bsp_Tango.pov

chess2.pov

cornell.pov

diffract.pov

diffuse_back.pov

float5

gamma_showcase.pov

grenadine.pov

isocacti.pov

mediasky.pov

patio-radio.pov

subsurface.pov

wallstucco.pov

C++

Some areas could only be added POV specific functionality using C++ :

blender/editors/space_text/text_format_pov.c

blender/editors/space_text/text_format_ini.c

Mspace_text.c

Mtext_format.h

Mtext_format_ini.c

Mtext_format_pov.c

and the MCMakelists.txt had to be modified to include added files.


But in Blender, all of the user interface is written in Python, that's why the file scripts/startup/bl_ui/space_text.py also has been updated even though it lies outside the usual addons folder:

# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8-80 compliant>
import bpy
from bpy.types import Header, Menu, Panel
from bpy.app.translations import pgettext_iface as iface_


class TEXT_HT_header(Header):
    bl_space_type = 'TEXT_EDITOR'

    def draw(self, context):
        layout = self.layout

        st = context.space_data
        text = st.text

        row = layout.row(align=True)
        row.template_header()

        TEXT_MT_editor_menus.draw_collapsible(context, layout)

        if text and text.is_modified:
            sub = row.row(align=True)
            sub.alert = True
            sub.operator("text.resolve_conflict", text="", icon='HELP')

        row = layout.row(align=True)
        row.template_ID(st, "text", new="text.new", unlink="text.unlink", open="text.open")

        row = layout.row(align=True)
        row.prop(st, "show_line_numbers", text="")
        row.prop(st, "show_word_wrap", text="")
        row.prop(st, "show_syntax_highlight", text="")

        if text:
            osl = text.name.endswith(".osl") or text.name.endswith(".oso")

            if osl:
                row = layout.row()
                row.operator("node.shader_script_update")
            else:
                row = layout.row()
                row.operator("text.run_script")

                row = layout.row()
                row.active = text.name.endswith(".py")
                row.prop(text, "use_module")

            row = layout.row()
            if text.filepath:
                if text.is_dirty:
                    row.label(text=iface_("File: *%r (unsaved)") %
                              text.filepath, translate=False)
                else:
                    row.label(text=iface_("File: %r") %
                              text.filepath, translate=False)
            else:
                row.label(text="Text: External"
                          if text.library
                          else "Text: Internal")


class TEXT_MT_editor_menus(Menu):
    bl_idname = "TEXT_MT_editor_menus"
    bl_label = ""

    def draw(self, context):
        self.draw_menus(self.layout, context)

    @staticmethod
    def draw_menus(layout, context):
        st = context.space_data
        text = st.text

        layout.menu("TEXT_MT_view")
        layout.menu("TEXT_MT_text")

        if text:
            layout.menu("TEXT_MT_edit")
            layout.menu("TEXT_MT_format")

        layout.menu("TEXT_MT_templates")


class TEXT_PT_properties(Panel):
    bl_space_type = 'TEXT_EDITOR'
    bl_region_type = 'UI'
    bl_label = "Properties"

    def draw(self, context):
        layout = self.layout

        st = context.space_data

        flow = layout.column_flow()
        flow.prop(st, "show_line_numbers")
        flow.prop(st, "show_word_wrap")
        flow.prop(st, "show_syntax_highlight")
        flow.prop(st, "show_line_highlight")
        flow.prop(st, "use_live_edit")

        flow = layout.column_flow()
        flow.prop(st, "font_size")
        flow.prop(st, "tab_width")

        text = st.text
        if text:
            flow.prop(text, "use_tabs_as_spaces")

        flow.prop(st, "show_margin")
        col = flow.column()
        col.active = st.show_margin
        col.prop(st, "margin_column")


class TEXT_PT_find(Panel):
    bl_space_type = 'TEXT_EDITOR'
    bl_region_type = 'UI'
    bl_label = "Find"

    def draw(self, context):
        layout = self.layout

        st = context.space_data

        # find
        col = layout.column(align=True)
        row = col.row(align=True)
        row.prop(st, "find_text", text="")
        row.operator("text.find_set_selected", text="", icon='TEXT')
        col.operator("text.find")

        # replace
        col = layout.column(align=True)
        row = col.row(align=True)
        row.prop(st, "replace_text", text="")
        row.operator("text.replace_set_selected", text="", icon='TEXT')
        col.operator("text.replace")

        # settings
        layout.prop(st, "use_match_case")
        row = layout.row(align=True)
        row.prop(st, "use_find_wrap", text="Wrap")
        row.prop(st, "use_find_all", text="All")


class TEXT_MT_view(Menu):
    bl_label = "View"

    def draw(self, context):
        layout = self.layout

        layout.operator("text.properties", icon='MENU_PANEL')

        layout.separator()

        layout.operator("text.move",
                        text="Top of File",
                        ).type = 'FILE_TOP'
        layout.operator("text.move",
                        text="Bottom of File",
                        ).type = 'FILE_BOTTOM'

        layout.separator()

        layout.operator("screen.area_dupli")
        layout.operator("screen.screen_full_area")
        layout.operator("screen.screen_full_area", text="Toggle Fullscreen Area").use_hide_panels = True


class TEXT_MT_text(Menu):
    bl_label = "Text"

    def draw(self, context):
        layout = self.layout

        st = context.space_data
        text = st.text

        layout.operator("text.new")
        layout.operator("text.open")

        if text:
            layout.operator("text.reload")

            layout.column()
            layout.operator("text.save")
            layout.operator("text.save_as")

            if text.filepath:
                layout.operator("text.make_internal")

            layout.column()
            layout.operator("text.run_script")


class TEXT_MT_templates_py(Menu):
    bl_label = "Python"

    def draw(self, context):
        self.path_menu(
            bpy.utils.script_paths("templates_py"),
            "text.open",
            props_default={"internal": True},
        )


class TEXT_MT_templates_osl(Menu):
    bl_label = "Open Shading Language"

    def draw(self, context):
        self.path_menu(
            bpy.utils.script_paths("templates_osl"),
            "text.open",
            props_default={"internal": True},
        )

class TEXT_MT_templates_pov(Menu):
    bl_label = "POV-Ray Scene Description Language"

    def draw(self, context):
        self.path_menu(
            bpy.utils.script_paths("templates_pov"),
            "text.open",
            props_default={"internal": True},
        )

class TEXT_MT_templates(Menu):
    bl_label = "Templates"

    def draw(self, context):
        layout = self.layout
        layout.menu("TEXT_MT_templates_py")
        layout.menu("TEXT_MT_templates_osl")
        layout.menu("TEXT_MT_templates_pov")


class TEXT_MT_edit_select(Menu):
    bl_label = "Select"

    def draw(self, context):
        layout = self.layout

        layout.operator("text.select_all")
        layout.operator("text.select_line")


class TEXT_MT_format(Menu):
    bl_label = "Format"

    def draw(self, context):
        layout = self.layout

        layout.operator("text.indent")
        layout.operator("text.unindent")

        layout.separator()

        layout.operator("text.comment")
        layout.operator("text.uncomment")

        layout.separator()

        layout.operator_menu_enum("text.convert_whitespace", "type")


class TEXT_MT_edit_to3d(Menu):
    bl_label = "Text To 3D Object"

    def draw(self, context):
        layout = self.layout

        layout.operator("text.to_3d_object",
                        text="One Object",
                        ).split_lines = False
        layout.operator("text.to_3d_object",
                        text="One Object Per Line",
                        ).split_lines = True


class TEXT_MT_edit(Menu):
    bl_label = "Edit"

    @classmethod
    def poll(cls, context):
        return (context.space_data.text)

    def draw(self, context):
        layout = self.layout

        layout.operator("ed.undo")
        layout.operator("ed.redo")

        layout.separator()

        layout.operator("text.cut")
        layout.operator("text.copy")
        layout.operator("text.paste")
        layout.operator("text.duplicate_line")

        layout.separator()

        layout.operator("text.move_lines",
                        text="Move line(s) up").direction = 'UP'
        layout.operator("text.move_lines",
                        text="Move line(s) down").direction = 'DOWN'

        layout.separator()

        layout.menu("TEXT_MT_edit_select")

        layout.separator()

        layout.operator("text.jump")
        layout.operator("text.start_find", text="Find...")
        layout.operator("text.autocomplete")

        layout.separator()

        layout.menu("TEXT_MT_edit_to3d")


class TEXT_MT_toolbox(Menu):
    bl_label = ""

    def draw(self, context):
        layout = self.layout

        layout.operator_context = 'INVOKE_DEFAULT'

        layout.operator("text.cut")
        layout.operator("text.copy")
        layout.operator("text.paste")

        layout.separator()

        layout.operator("text.run_script")


classes = (
    TEXT_HT_header,
    TEXT_MT_edit,
    TEXT_MT_editor_menus,
    TEXT_PT_properties,
    TEXT_PT_find,
    TEXT_MT_view,
    TEXT_MT_text,
    TEXT_MT_templates,
    TEXT_MT_templates_py,
    TEXT_MT_templates_osl,
    TEXT_MT_templates_pov,
    TEXT_MT_edit_select,
    TEXT_MT_format,
    TEXT_MT_edit_to3d,
    TEXT_MT_toolbox,
)

if __name__ == "__main__":  # only for live edit.
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

Notice that the special command line

   # <pep8-80 compliant>

writes pep-80 rather than pep-8 as getting closer to the native blender program often requires more demanding standards : pep-80 asks for even shorter line lengths and so a file passing the pep-80 automated tests that the Blender Foundation regularly runs might fail passing PEP-80.