Thursday, 31 October 2024

VR Project Week 2 - Hercules

     This week I continued working on the Hercules VR project. My main task was to update the hands to include all of the poses in the game, as well as look into setting up an ik rig to handle wrist rotations.

https://www.youtube.com/watch?v=EKR8ogonD68 

    I started watching this video to set up the FABRIK nodes. I had some issues but solved it by changing the rotation space to bone space. This got me the majority of the way but I had issues with the rotation of the wrist twisting around and pinching. This specific tutorial had me position the arms underneath the camera in the hierarchy of the vr pawn, instead of underneath the motion controller grips. I used the transform bone node to continue adjusting the wrists rotation. I got it to a working condition but then we had checked it with the snapping of the shields as well as the spears snap and the snap pivot was different. We were not able to diagnose the problem and had to switch back to the original hands without ik. I continued working on the poses and set them all up inside of the anim blueprint and also the VR pawn blueprint. 

Here is an additional vr youtube tutorial I watched to help me out.  

https://www.youtube.com/watch?v=EKR8ogonD68 







Tuesday, 29 October 2024

Learning Python with Houdini Part 4

 Classes

    Classes are just a collection of data and methods to operate on that data.
    Object Oriented Programming encapsulates data (attributes_ and functions (behvaiors) into components called classes. An object is a instance of a data class. Classes hide data while exposing an interface to the outside world. Invoking classes creates a new object. 
    Python functions stuck inside a class are called methods.
    Blueprints in Unreal and Prefabs in Unity are classes. Digital Assets in Houdini are also the same. 
    Format of a class-
Class className:
    '''if you put triple quotes here it'll auto document this class'''
    def __init__(self):           #Constructor Method
        code_block
    def method (self, anArgument=Default):
        codeBlock
    def anotherMethod(self, arg1 = default2, arg2 = default3)
        codeBlock

    Self is a pointer/memory address that points to the class. The init function will fire off first when the class is first run. The first argument inside of every method is self.
    Tip: the triple quotes there will show up when you're writing it in the python shell, it will pop up when you're writing the function.
    To create an instance of a class, just assign the class/ constructor to a variable
myInstance = myCustomClass()
    You should be putting default values in all of you definitions, that way you can call the class without any arguments.
    When defining the constructor (the init function), it's good to define all the variables that you're going to use for the class.
    Try starting out with a function that prints out a statement to test if it works.
Note: If you forget to put the self in the function parameter inside of a class, it's not going to be able to find it. Without .self, it creates a local variable, but none of the functions in the rest of the class will be able to access it. It needs the self. to point to the place in memory the class resides, to make the variable an attribute of the class to be accessed later. 



    We put :02 right after the variable to format it so it is padded to 2 digits (will write out 01 instead of just 1).
    Now test the class by calling it.
    What if you wanted to set variables inside the function when you create an instance of the class, you can put in arguments in the init function
    Still good practice to give default values to the arguments. We can go further than this by creating a function that sets the values of our variables that we can call to change them on the fly.

    So we can use this function within the class, but we can also call upon that function outside of that function. 

    Public Vs. Private

    All attributes are available to everyone. There is a way of making something kind "Private"
self.__onlyMe = "Foobar eh, Feeterbeeb?"
    Naming an attribute with a "__" (double underscore) prefex scrambles the name. This is called a Dunder (derived from monty python jokes, just like how this entirely language is named after). Used to create private/exclusive classes)
    
    If you run this function normally it works fine and prints and manipulates the variables in the setTime function fine. But the trouble only comes when you try to call that specific variable. 


    This is going to throw an error and say there's no attribute named that. if you really want to access a 'private variable', you have to write it like this:
    
What if you wanted a variable that all instances of a class shares, but none of them control? Well you can do that by defining it outside of the init function. 


    This is only in the class, not in the instance, this is useful for creating multiple instances that share one variable and all of them have access to it, they just wouldn't be in charge of it. So by running this:
    You get this:


    Count printed 2 because you've created 2 instances of the class and it incremented every time you created it. You could have asked to print tiktok.count and it would have the same value.

Downloading and using Python libraries
    Find the houdini folder in your documents folder. Mine was named houdini20.5. Jump inside of it, and if there is not alread a folder named python3.11libs, create one. Then download the py file that you want. We tested it with Time.py file given to us. Once you've done that restart Houdini, and open up the Python Shell. You'll notice you can import the file without errors and use all the functions from that library.


    

Thursday, 24 October 2024

VR Project Week 1 - Hercules

     This week we started the F stage of blocking out our VR project. I was responsible for rigging the hands and Hercules.

    Unfortunately I had trouble with getting the hand poses working in the game, the animation blueprint wasn't working. 


base model

grasp

Hercules FK skeleton


Inside of Unreal






Learning Python with Houdini Part 3

Data Types

    Mutable Vs. Immutable Types

    Immutable data types are sequences that can't be change. If you want to change the value, Python destroys the old version in memory and creates a new version. This is fast.

    Mutable are sequences that can be changed. If I have a chunk of memory, I can expand or contract my memory to include what I need. You need a lot of overhead for this and it is slow. Use this if you don't know how big the data is going to be and there could be expansion or contraction.

    Sequences are any collections of objects in Python. 

    Immutable:

  • Boolean
  • Integer
  • Floating point
  • Complex
  • String
  • Tuple (uses () to show it's a tuple)
  • Frozen Set 
    Mutable:
  • List (uses [] to show it's a list)
  • Dictionary (uses {} to show it's a dictionary)(great way of organizing your data, html and json files use dictionaries)
  • Set (there are no duplicates in sets)(use{}) (hybrid between List and Dictionary)
    How are integers and floating points and strings immutable if we've been able to change them in previous assignments? Well you can test this by assigning a variable as a string, and then try to change it like you would try to change a List. 

    In a List you use a reference number to specify the address you wish to change in a sequence (you start at 0 because it's the starting address in memory + 0). You can't do this with immutable types because you can't change that specific place in memory.
    Another thing with indexing is that you can go backwards in memory (when you go backwards, you start at -1 to get the very end)
    You can even use a range for you index, or specify you want to skip every second item.
    Lists don't have to have items with the same data type. You can have strings and integers. 
    For Dictionarys, you need a key and a value. A key is an address, but not an index. The only condition for a key is that it cannot be a mutable instance. You can have duplicate values but not duplicate keys. A value is any kind of value. The syntax is like this:
{key:value, key:value}

Notice that len() returns 2 elements, that is because it goes by the key, not what's in the dictionary. To access the elements you have to use the key.

    As a suggestion, keep all of your types inside of you sequences the same to make your life easier.
    If you want to add a new element to dictionaries, you can't append. The keys are not necessarily sorted. Keys are something called hashes (used to make data more compact). You need to define the key and then assign the data.
    You can iterate over a dictionary but it will not iterate over a predictable order because of the keys (due to the hashes)
Dictionary Methods:


    To create an empty Tuple, you just have parenthesises.
gal = ()
    This returns a value of none (). Why would you do this? Sometimes programmers like to use this when they're creating functions to remind themselves/others that that variable is important. 
    List Methods

    When you use input(), it returns a string. We use .split() to unpack the input with several variables.
    Sets are a hybrid of Dictionaries and Lists. It will change the order on how you inputted the values, dependent upon the hash. 
    You can check in there is a element in the set with:
    You cannot have duplicate elements inside of a set.

Recursion

    Recursion can be slow on your computer, because it is calling itself over and over again.
    Fractals: infinitely defined.
    3 units of recursion to help create any realistic type of visual effects. You need one fractal shape, that inside has several levels of recursion. 

Tuesday, 22 October 2024

Learning Python with Houdini Part 2

 This week we're focusing on functions and how to make them.

  • Function: re-callable code used to perform basic tasks
  • Module: a file that contains functions and classes
  • Package: multiple modules assembled together in one collection (like a directory folder)
Python only includes stuff it needs. So if you need something extra you have to import them yourself
import moduleName
moduleName.function(arguments)

This is an example of how to import and then call a function. Module name is called Name Space, it references which module it is from. We could also import just the functions we want from them.
from moduleName import identifier(, indentifierB, idenifierC)
from moduleName import *
    Identifier = function contained in module. That last line includes a wildcard (*), and what happens is that now it imports all those functions and says you do not need to include module name anymore when calling functions. 
    If you wanted to change the module name, you can do:
import math as foo
foo.cos(pi)
output: -1.0 (cosine of pi is -1.0)
    You can even change the individual function names as well
from math import fmod as snafu
snafu(5,12)
output: 5.0

Random Module

    We're going to be using the random module for this next assignment. 
import random 
print(random.random())
    If you call this script it will always give you a random number. If you want it to be a predictable random number, you need to set the seed first.
random.seed(84.76)
print(random.random())
    This will give you the same random number every time. (Guess it's not really random but this can be useful). If you want a random number inbetween a minimum and a maximum, you can use randuniform or randint (will only give integers).
random.uniform(-30,-10)
random.randint(2,20)
random.randrange(3, 9,2)
    randrange will give you every second value in between 3 and 9 (so only 3, 5, 7, 9)
random.choice('FUBAR')
    Choice will give a random value from the sequence you give it.

Declaring Functions

def function-name(parameter-list):
    statements
    return someValue
     The basic structure of a function. It doesn't always have to return a value.

Recreating the clamp function

def clam(inputValue, min, max):
    result = inputValue
    if result < min:
        result = min
    if result>max:
        result = max

    return result
 
Potentially this could work with strings.

Scope of variables

    If you make a variable inside a function, it exists only within the function that's within the scope of that function, this is a local variable.
    If you make a variable outside of a function, it is a global function and can be accessed anywhere.
    If you are inside of a function, you can specify if you want to use the global variable

    The variable inside of the b function will manipulate the global value of foo. 
    You can set default values in functions if the function is called and doesn't give any values
def volume (length=1, width=1, depth =1)
    return length*width*depth

print(volume())

    This will output 1. You can also change the order you put the variables in

print(volume(depth=5, length=3, width=2)


Recursion

    A recursive function is a function that calls itself either directly or indirectly. It must include a base case which is the simplest case to solve for (if it doesn't hit this base case then it will continue infinitely). Must also contain a recursive call to itself. An example is a factorial function:
def fact(number):
    if number <1:
        return 1
    else:
        return number *fact(number-1)
    This will call the factorial function, each time decrementing the number until it is less than 1.
    Here's a bonefinder function that searches for all of the bones in a houdini object.





 

Learning Python with Houdini Part 1

Running Scripts Inside of Houdini

    You can either run python commands directly in the Windows > Python Shell. 


    Or you can create a new shelf to store your new scripts. Click on the plus in the top toolbar to create a new shelf, and then right click on the shelf to create a new tool. 

Getting Input

Note:
Houdini seems to have an issue if you try to get input from the issue that lasts multiple lines. To ensure that Houdini won't crash, just get all user input from one line. 
To get user input:
inputvar = input("Enter first and second Values:")

Once you have your input, you need to split up all of the inputs from that one input variable. To split up your values, use split()
first, second = input.split()

    This will get you the first input value into the variable 'first' and the second input value into the variable 'second'. Now to change the input string into integers to be used, use int()
first = int(first)

Strings

    You can delineate a variable as a string by using single quotes (') or double quotes (").

    var = "this is a string" or var = 'this is a string'

    If you want to include a single quote or a double quote when you're using them to contain the string, you have to use an escape character to tell the computer to use the raw ASCII value for it

print( 'you\'re reading this')

output:  you're reading this

    If you want to include a backwards slash you also have to include a backwards slash right before it. (This is called an escape character)

    When printing something you can format it to include a variable.

    var = 23

    print(f"this is a string with {var}")

outputs: this is a string with 23

You can also perform calculations within the curly brackets

    print(f"this is a string with {var*3}")

    outputs: this is a string with 69

You can also just put calculations without variables inside the curly brackets.

    print(f"this is a string with {4%6}")

    outputs: this is a string with 4

    Sometimes from old python videos they'll use the str.format to include variables inside print functions.

    foo = 'FUBAR'

    print("this is my str.format with {}and {}".format(foo, var))

    outputs: this is my str.format with FUBAR and 23

This can also be used to specify which order the variables appear in by putting numbers into the curly brackets.

    print("this is my str.format with {1} and {0}".format(foo, var))

    outputs: this is my str.format with 23 and FUBAR

Printing type of variables

You can specify that you want to print out what the variable type is in python.
obj = hou.node('/obj')
print(type(obj))
output: <class 'hou.OpNode'>
OpNode stands for Operator node

While Loops

    Use whiles if you know you're going to have brakes, however it's risky and can cause Houdini to crash if it gets stuck in an infinite loop.
    
    This code will run exactly 5 times.

For Loops

    For loops are useful for iterating over a certain amount of times.

    This was the script I had made for tech art's HW8 assignment. The user inputted the width, height, and length, and then based on that made a cube made up of the object type based on the user's input. 

Starting out Script in Houdini

Houdini works with old school folder structure. the Obj is the top folder level, the 'root'. When you create a Geometry Node, it's a container for more folders. 

By default the houdini library does not come with python, you have to specifically import it. (If you are running your script inside houdini's tools, it will automatically import the houdini library for you. Anywhere outside of that you need to import it yourself)

import hou

    When you're making scripts, you'll want to grab the root level. 

root = hou.node('/obj')

    We assigned the path '/obj' to the variable root. 

    Next we'll create a container geometry node.

container = root.createNode('geo', 'sphereGeo')

    The variable container has a pointer to the node named 'sphereGeo'. We made sure to put in 'geo' first as that is the type of node we want to create (a geometry node to hold our geometry). Now we want to create a sphere inside the container.

mysphere = container.createNode('sphere', 'classSphere')

    This will create a primitive sphere inside of our sphereGeo node. We're using the container node here because it is going to be the parent of the sphere object. (we want the sphere inside the container node).

    You can create a sphere node and then a color node, but to connect them, you need to use the setInput function.

colorNode.setInput(0, mysphere)

colorNode.setDisplayFlag(True)

    This connects the mysphere output to the colorNode's input, as well as setting the display flag on the colorNode to see it. 

    If you want to change parameters in Houdini, you need to use the .parm and .set function to do so.

child.parm('type').set(2)

    You specify which parameter you want to manipulate in the first parenthesizes, and then the second set is what you want to set it to. This sets the primitive Type of the sphere to Polygon Mesh. It is 2 because you can think of this as an array, we want the third value in the array. 


    For color, we need to use .parmTuple because there is more than one value in the color parameter

colorNode.parmTuple("color").set((1.0, 0.0, 0.0))

    This sets the color to a bright red. Note that you also need the double parenthesizes for the set or else it will not work.

Monday, 14 October 2024

Converting Houdini Files into Code

 If you need to convert a file from Houdini Indie to Commercial, you can use python to convert the entire Houdini File into a python script and then run it inside a new file.

    Inside the file you want to convert, open up the python shell window (Windows>Python Shell)


The File I want to copy, I have two objects so I'll do them one at a time

    You're going to want to make a variable that contains a reference to your Houdini Object, so in the node view, left click on your Houdini Object and drag it into the Python Shell. The variable can be named anything just make sure you remember it. 


    Hit enter to go to the next line. Now we want a pointer to where we want to save the file. So type:

 outfile = open(r"YourFilePathHere\NameForFile.py", "w")
    The r is to make sure the file path is in raw in case you have any spaces in it. We also need the "w" to tell python that it is in write mode.

In my Temp Folder

    We've created a py file, but nothing is inside it. Now we're going to write to the file. 

outfile.write(foo2.asCode(recurse=True))

See how it now has data inside the Python File

    If you look into the file, you'll see code has now been generated. 


To Run this code now, open a new Houdini File. You'll be making a Shelf button to run this. Right click on your shelf (make a new one if you need to for organization), and click "New Tool"


    Name and Label it as you like. Then go to the script tab. You'll want to open up the py file you just made, Ctrl+A to select everything and copy and paste it into the script tab. 

Copy and Paste everything into here
    
    Hit Apply and you should be done. Now clicking on this shelf button should generate everything in the previous Houdini File. 
I had two scripts to run and they both worked


Illumination Models

 Basic Lighting Models The Three Illumination Models: ambient: color of objects in the scene with no direct light on it (When it is in shado...