POV-Ray : Newsgroups : povray.programming : <no subject> Server Time
28 Mar 2024 07:52:55 EDT (-0400)
  <no subject> (Message 1 to 1 of 1)  
From: Bruno Cabasson
Subject: <no subject>
Date: 27 Feb 2022 05:45:00
Message: <web.621b5609c23a6d97283012224d82e4@news.povray.org>
Hi There!

+------------------+
| PART 1 : general |
+------------------+

I am a bit sad to see that the golden age of POV-Ray is behind us. POV-Ray
remains a great thing, still unequaled. My deepest wish concerning it is that it
finds a new interest among the community but also outside it.

A new interest in at least 2 respects.

First, as a powerful language. I found nothing equivalent and THAT compact to
describe scenes. As a language, using POV-ray and taking advantage of all its
syntactic capabilities is all about programming. This is its strength, praised
by all of us. But it is also a weak point because 3D artists are not programmers
(to my opinion). So, if the language itself were more 'accessible' to the many,
perhaps artists would be less reluctant to dive into POV-Ray. To give it a new
interest, making the language itself more 'attractive' would serve POV-Ray's
purpose. That's where my object-oriented attempt with the still embryionic PROOF
 kicks in. I'll explain the main ideas below.

Second, it is an extremely good renderer. Provided you have been POVing for
years, you can generate the best images ever. Now, with modern machines,
rendering time is less of a great concern, even without using GPUs.

Third, POV-Ray lacks all kinds of libraries : objects, materials, templates,
animation oriented features, .... It also lacks on-line tutorials. This could
make it more popular.

I can see one opening towards the 3D people : Blender. There is an interface
between POV-Ray sdl and Blender. Blender is sooo much widespread that it could
help our purpose (and that of Blender also. Presently, I cannot see how.

Also, we still have among us a bunch of masters. They could help promoting if
ever they happend to appear again...


+------------------+
| PART 2 : classes |
+------------------+

Well, that being said, here is a quick glance at PROOF.


    +------------------------+
    | PART 2.1 : sample code |
    +------------------------+

After having #include'ed the appropriate definitions (currently, I have :
primitives.inc, collections.inc, oocore.inc for the basic OO featues), I can
write this, as a syntactic example without any other interest :

[START]
// == class MyClass =========================
#declare MyClass = class
(
    "MyClass", // Name of the class
    no,        // No parent class
    None,
    array      // properties
    {
        array mixed {"property1", TYPES._int_},
        array mixed {"property2", TYPES._string_}
    },
    array    // Methods
    {
        "method1",
        "method2"
    }
)

// Define 2 overloaded constructors used through the 'new()' macro in
"oocore.inc".
// Default constructor, with signature number = 0.
//
// new_MyClass(this, property1, property2)
#macro new_MyClass(this, optional params_dict)
    #ifdef (local.params_array)
        #local this.property1 = check(params_dict.property1, TYPES._int_);
        #local this.property2 = check(params_dict.property2, TYPES._string_);
    #else
        #local this.property1 = 0;
        #local this.property2 = "default string";
    #end
#end

// Overloaded constructor #1, with signature number = 1.
// new_1_MyClass(this, property1, property2, _offset)
#macro new_1_MyClass(this, optional params_dict)
    // By hand : check parameter types, then link methods to the class to get
the instance.
    #local p1 = check(params_dict.property1, TYPES._int_);
    #local p2 = check(params_dict.property2, TYPES._string_);
    #local offset_ = check(params_dict.offset_, TYPES._vector_);

    #local this.property1 = p1 + vlength(offset_);
    #local this.property2 = concat(p2, " ", vstr(5, offset_, ",", 0, 0));
#end


// Instance dump method, used through the 'dump' macro in "oocore.inc".
#macro MyClass_dump(this, indent_level)
    #debug concat(indent[indent_level], "instance of \"", this.class.classname,
"\"\n")
    #debug "{\n"
    #debug concat(indent[indent_level+1], ".class : ", this.class.classname,
"\n")
    #debug concat(indent[indent_level+1], ".property1 : ", str(this.property1,
0, 0), "\n")
    #debug concat(indent[indent_level+1], ".property2 : ", this.property2, "\n")
    #debug concat(indent[indent_level+1], ".method1() : ", this.method1, "\n")
    #debug concat(indent[indent_level+1], ".method2() : ", this.method2, "\n")
    #debug "}\n"
#end


// Default 'method1' method, used through the 'call' macro in oocore.inc".
// MyClass_method1(this, p1, p2)
#macro MyClass_method1(this, optional params_dict)
    #local p1 = check(params_dict.property1, TYPES._int_);
    #local p2 = check(params_dict.property2, TYPES._string_);

    #debug concat("==> ", this.method1, "(p1 = ", str(p1, 0, 0), " (int), p2 =
", p2, " (string))\n")

    #debug "executing ...\n"
    #debug concat("this.property1 = ", str(this.property1, 0, 0), "\n")
    #debug concat("this.property2 = \"", this.property2, "\"\n")

    #debug concat("<== ", this.method1, "\n")
#end


// Overloaded 'method1' method #1, used through the 'call' macro in oocore.inc".
// MyClass_method1_1(this, p1, p2, p3)
#macro MyClass_method1_1(this, optional params_dict)
    #local p1 = check(params_dict.property1, TYPES._int_);
    #local p2 = check(params_dict.property2, TYPES._string_);
    #local p3 = check(params_dict.pigment_, TYPES._pigment_);

    #debug concat("==> ", this.method1, "_1(p1 = ", str(p1, 0, 0), " (int), p2 =
\"", p2, "\" (string), p3 = pigment {})\n")

    #debug "executing ...\n"
    #debug concat("this.property1 = ", str(this.property1, 0, 0), "\n")
    #debug concat("this.property2 = \"", this.property2, "\"\n")

    #debug concat("<== ", this.method1, "\n")
#end

// Default 'method2' method, used through the 'call' macro in oocore.inc".
// MyClass_method2(this, p1)
#macro MyClass_method2(this, optional params_dict)
    #local p1 = check(params_dict.p1, TYPES._vector_);

    #debug concat("==> ", this.method2, "(p1 = <", vstr(3, p1, ", ", 0, 0), ">
(vector)\n")

    #debug "executing ...\n"

    #debug concat("this.property1 = ", str(this.property1, 0, 0), "\n")
    #debug concat("this.property2 = \"", this.property2, "\"\n")

    #debug concat("<== ", this.method2, "\n")
#end
// ===========================================

// Using MyClass
describe(MyClass,)

// Get 2 instances of MyClass, using the 2 provided constructors.
#declare my_instance = new(MyClass, 0, dictionary {.property1 : 0, .property2 :
"text"});
dump(my_instance,)

#declare my_instance2 = new(MyClass, 1, dictionary {.property1 : 0, .property2 :
"text", .offset_ : x + y});
dump(my_instance2, )

// Call all the versions of the overloaded 'method1' method of class 'MyClass'.
call(my_instance.method1,, my_instance, dictionary {.property1 : 0, .property2 :
"text"})
call(my_instance.method1, 1, my_instance, dictionary {.property1 : 0, .property2
: "text", .pigment_ : pigment{Yellow}})

// Call all 'method3' method of class 'MyClass'.
call(my_instance.method2,, my_instance, dictionary {.p1 : <0, 1, 3>})


// == class Derived =========================
#declare Derived = class
(
    "Derived",
    yes,  // has a parent class
    MyClass, // Parent class
    array
    {
        array mixed {"property3", TYPES._vector_} // added property
    },
    array
    {
        "method3" // added method
    }
)

describe(Derived,)

// Instance dump method., used through the 'dump' macro in "oocore.inc".
#macro new_Derived(this, optional params_dict)
    #debug "==> new_Derived()\n"

    super(this,, params_dict)

    #ifdef (local.params_array)
        #local offset_ = check(params_dict.offset_, TYPES._vector_);
    #else
        #local offset_ = 0;
    #end
    #local this.property1 = this.property1 + vlength(offset_);
    #local this.property2 = concat(this.property2, " ", str(2*vlength(offset_),
0, 4));
    #local this.property3 = offset_;

    #debug "<== new_Derived()\n"
#end

#macro Derived_dump(this, indent_level)
    #debug concat(indent[indent_level], "instance of \"", this.class.classname,
"\"\n")
    #debug "{\n"
    #debug concat(indent[indent_level+1], ".class : ", this.class.classname,
"\n")
    #debug concat(indent[indent_level+1], ".property1 : ", str(this.property1,
0, 0), "\n")
    #debug concat(indent[indent_level+1], ".property2 : ", this.property2, "\n")
    #debug concat(indent[indent_level+1], ".property3 : <", vstr(3,
this.property3, ", ", 0, 6), ">\n")
    #debug concat(indent[indent_level+1], ".method1() : ", this.method1, "\n")
    #debug concat(indent[indent_level+1], ".method2() : ", this.method2, "\n")
    #debug concat(indent[indent_level+1], ".method3() : ", this.method3, "\n")
    #debug "}\n"
#end

#macro Derived_method2(this, params_dict)
    #debug "Derived_method2()\n"
#end

#macro Derived_method3(this, params_dict)
    #debug "Derived_method3()\n"
#end
// ===========================================

#declare my_derived_intance = new(Derived,, dictionary {.propery1 : 33,
..property2 : "zorg", .offset_ : <0,1,2>});
dump(my_derived_intance,)


// == class Derived2 =========================
#declare Derived2 = class
(
    "Derived2",
    yes,
    Derived,
    array {}, // no more properties
    array {}  // no more methods
)

describe(Derived2,)

// Instance dump method., used through the 'dump' macro in "oocore.inc".
#macro new_Derived2(this, optional params_dict)
    #debug "==> new_Derived2()\n"

    super(this,, params_dict)

    #debug "<== new_Derived2()\n"
#end

#macro Derived2_dump(this, indent_level)
    #debug concat(indent[indent_level], "instance of \"", this.class.classname,
"\"\n")
    #debug "{\n"
    #debug concat(indent[indent_level+1], ".class : ", this.class.classname,
"\n")
    #debug concat(indent[indent_level+1], ".property1 : ", str(this.property1,
0, 0), "\n")
    #debug concat(indent[indent_level+1], ".property2 : ", this.property2, "\n")
    #debug concat(indent[indent_level+1], ".property3 : <", vstr(3,
this.property3, ", ", 0, 6), ">\n")
    #debug concat(indent[indent_level+1], ".method1() : ", this.method1, "\n")
    #debug concat(indent[indent_level+1], ".method2() : ", this.method2, "\n")
    #debug concat(indent[indent_level+1], ".method3() : ", this.method3, "\n")
    #debug "}\n"
#end

#macro Derived2_method3(this, optional params_dict)
    #debug "Derived2_method3()\n"
#end

// ===========================================

#declare my_derived2_intance = new(Derived2,, dictionary {.property1 : 33,
..property2 : "zorg", .offset_ : <0,1,2>});
dump(my_derived2_intance, 0)
call(my_derived2_intance.method3,, my_derived2_intance,)

#debug concat("isinstance(my_derived2_intance, Derived2) : ",
str(instanceof(my_derived2_intance, Derived2), 0, 0), "\n")
#debug concat("isinstance(my_derived2_intance, Derived) : ",
str(instanceof(my_derived2_intance, Derived), 0, 0), "\n")
#debug concat("isinstance(my_derived2_intance, MyClass) : ",
str(instanceof(my_derived2_intance, MyClass), 0, 0), "\n")

// == class Junk =============================
#declare Junk = class
(
    "Junk",
    yes,
    Derived,
    array {}, // no more properties
    array {}  // no more methods
)

#macro Junk_dump(this, indent_level)
    Derived_dump(this, indent_level)
#end

#macro new_Junk(this, optional params_dict)
    super(this,, params_array)
#end

#debug concat("isinstance(my_derived2_intance, Junk) : ",
str(instanceof(my_derived2_intance, Junk), 0, 0), "\n")

describe(Junk,)

#declare my_junk = new(Junk,,)
dump(my_junk,)

[END]

    +-------------------+
    | PART 2.2 : output |
    +-------------------+

I find all this quite compact. It could be better if the native Sdl had some
specific features. But it does the job as it is. The output of the above code is
teh following :

class MyClass
{
    .classname : MyClass
    .property1 : int
    .property2 : string
    .method1()
    .method2()
}
instance of "MyClass"
{
    .class : MyClass
    .property1 : 0
    .property2 : default string
    .method1() : MyClass_method1
    .method2() : MyClass_method2
}
instance of "MyClass"
{
    .class : MyClass
    .property1 : 1
    .property2 : text 1,1,0,0,0
    .method1() : MyClass_method1
    .method2() : MyClass_method2
}
==> MyClass_method1(p1 = 0 (int), p2 = text (string))
executing ...
this.property1 = 0
this.property2 = "default string"
<== MyClass_method1
==> MyClass_method1_1(p1 = 0 (int), p2 = "text" (string), p3 = pigment {})
executing ...
this.property1 = 0
this.property2 = "default string"
<== MyClass_method1
==> MyClass_method2(p1 = <0, 1, 3> (vector)
executing ...
this.property1 = 0
this.property2 = "default string"
<== MyClass_method2
class Derived
{
    .classname : Derived
    .parent :
        class MyClass
        {
            .classname : MyClass
            .property1 : int
            .property2 : string
            .method1()
            .method2()
        }
    .property3 : vector
    .method3()
}
==> new_Derived()
<== new_Derived()
instance of "Derived"
{
    .class : Derived
    .property1 : 0
    .property2 : default string 0.0000
    .property3 : <0.000000, 0.000000, 0.000000>
    .method1() : MyClass_method1
    .method2() : Derived_method2
    .method3() : Derived_method3
}
class Derived2
{
    .classname : Derived2
    .parent :
        class Derived
        {
            .classname : Derived
            .parent :
                class MyClass
                {
                    .classname : MyClass
                    .property1 : int
                    .property2 : string
                    .method1()
                    .method2()
                }
            .property3 : vector
            .method3()
        }
}
==> new_Derived2()
==> new_Derived()
<== new_Derived()
<== new_Derived2()
instance of "Derived2"
{
    .class : Derived2
    .property1 : 0
    .property2 : default string 0.0000
    .property3 : <0.000000, 0.000000, 0.000000>
    .method1() : MyClass_method1
    .method2() : Derived_method2
    .method3() : Derived2_method3
}
Derived2_method3()
isinstance(my_derived2_intance, Derived2) : 1
isinstance(my_derived2_intance, Derived) : 1
isinstance(my_derived2_intance, MyClass) : 1
isinstance(my_derived2_intance, Junk) : 0
class Junk
{
    .classname : Junk
    .parent :
        class Derived
    .parent :
        class Derived
        {
            .classname : Derived
            .parent :
                class MyClass
                {
                    .classname : MyClass
                    .property1 : int
                    .property2 : string
                    .method1()
                    .method2()
                }
            .property3 : vector
            .method3()
        }
}
==> new_Derived()
<== new_Derived()
instance of "Junk"
{
    .class : Junk
    .property1 : 0
    .property2 : default string 0.0000
    .property3 : <0.000000, 0.000000, 0.000000>
    .method1() : MyClass_method1
    .method2() : Derived_method2
    .method3() : Derived_method3
}

+---------------------------+
| PART 3 : object in scenes |
+---------------------------+

Now, we have classes and some OO features. I went a bit further with objects in
scenes (spheres, boxes, and all others). I designed a set of classes for that
puspose, and the use of them leads to a quite compact code. The definitions are
in a specific include file. As a teaser,, here is a code fragment :

describe(Sphere,)
#declare my_sphere = new
(
    Sphere,,
    dictionary {.center : 1.2*x, .radius_ : 1}
)
setmods
(
    my_sphere,
    dictionary
    {
        .texture_ : pigment {Red},
    }
)
setfinish(my_sphere, finish {specular 0.8 roughness 0.002})
setnormal(my_sphere, normal {crackle 2 scale 0.3})
dump(my_sphere,)
spawn(my_sphere)
dump(my_sphere,)

And let there be the sphere !!

As such, classes for object hav no real interest if you just drop objects in
your scene. But, with classes and object-orientedness, one can go further and
implement behaviours, design pattens ...

This post was quite long, therfore, I'll tell more in a next one.

Bruno


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.