1
1

Compare commits

...

451 Commits

Author SHA1 Message Date
4a801f6c6f Merge branch 'master' into object_nodes 2016-08-09 16:37:15 +02:00
49c63d46db Merge branch 'master' into object_nodes 2016-07-25 09:35:03 +02:00
be12e7a42e Added a number of addition matrix manipulation nodes. 2016-07-24 13:09:35 +02:00
dcb4ca500f Merge branch 'master' into object_nodes 2016-07-19 11:43:07 +02:00
41cc366010 Merge branch 'master' into object_nodes 2016-07-19 10:53:34 +02:00
794cbb2790 Minor whitespace fix. 2016-06-26 12:45:51 +02:00
0dbafd1fe5 Added object nodes for looking up an object and transform from global data. 2016-06-24 16:19:23 +02:00
c97a25b206 LLVM TypeBuilder and copy function for node_counted_ptr.
Using a DerivedMesh directly for mesh data isn't so great because
the lifetime of a DM is not necessarily known at compile time, depending
on branching. node_counted_ptr is basically a simplified shared_ptr variant.
2016-06-24 14:45:53 +02:00
af27ce2497 Use a hash table to register external functions with the LLVM engine.
This will allow using BLI/BKE code for complex data types, such as DerivedMesh.
2016-06-24 10:23:30 +02:00
0f868ea6b6 Include mesh opcode functions in modules.
Modifier functions with variable expression inputs are disabled,
until DerivedMesh handling through LLVM is implemented.
2016-06-24 10:23:03 +02:00
0f9c5bdb5c Fix graph return values: Not all value types are returned as duals. 2016-06-23 19:16:40 +02:00
11bbcce929 Fix for graph function signature: use pointers also for aggregate and struct types. 2016-06-23 18:36:10 +02:00
0be2c2a89a Use the LLVM backend for modifier nodes.
This currently fails, because the actual mesh nodes are not yet implemented
in the LLVM backend.
2016-06-23 13:43:25 +02:00
cf27fdb659 Reduce repetitive code in the C API by defining static node graph signatures.
Node graph inputs/outputs are now defined statically and graph instances
can be constructed by passing these as ArrayRefs. The code for compiling
various node graph types can then be unified.
2016-06-23 12:36:05 +02:00
48038ee8a5 More codegen implementations for basic functions.
* float/int conversion
* float3/float4 getters and setters
2016-06-22 18:25:37 +02:00
d86efe5830 Removed unused source file. 2016-06-22 12:35:57 +02:00
d2e2ec766f Use load/store for float3 and float4 to generate a bit nicer code.
It's unclear if the same can be done for larger aggregate types like matrix44.
2016-06-21 17:42:27 +02:00
13a84fcb33 Define names for most values in generated LLVM code.
These are purely for debugging the code, they help a lot in reading
LLVM dumps.
2016-06-21 16:50:04 +02:00
e4dea76bdd New CodeGenerator implementation for replacing the old BVM compiler. 2016-06-20 17:38:37 +02:00
075f60c6f5 Moved old bvm codegen and function files into bvm module.
The intern and compile modules are now truly independent from backends (llvm and bvm).
2016-06-19 14:50:05 +02:00
ff3c066324 Refactor of the node graph compiler class, to have a backend-agnostic base.
The main compiler class is now itself independent of LLVM and can be used for
future backends. The implementation of specific behavior (values, statements, etc.)
happens through a CodeGenerator implementation. This also uses aggregation rather
than inheritance on the compiler class part (which is not a virtual class itself).
2016-06-18 12:41:06 +02:00
e197ce618b Merge branch 'master' into object_nodes 2016-06-16 09:18:40 +02:00
926819d82f Use a Scope concept for avoiding codegen for existing nodes.
Rather than a single socket value map, use a "scope" to keep track of which
nodes are already generated. This reintroduces the "block" idea from earlier
versions, but at the later codegen stage rather than the node graph.

Describing scope on the level of nodes becomes quite clumsy and ambiguous,
and moves nodes further away from the UI representation. The compiler can
handle this more elegantly without requiring persistent data.

Scopes can be nested. This is not used yet, but will allow scoped variables
later on, where the variables being used for a node path depend on its scope.
2016-06-16 09:10:35 +02:00
5963c56967 Removed/Disabled some deprecated code. 2016-06-12 14:50:54 +02:00
989810f312 Disabled compiling and eval of broken BVM functions, to simplify debugging until they can be reimplemented. 2016-06-10 11:07:55 +02:00
6666b84182 Use persistent pass managers for optimization, rather than creating them every time. 2016-06-09 16:11:18 +02:00
c590f30623 Set a target triple and data layout for modules.
According to the site below, this is necessary for proper optimization.

http://llvm.org/docs/Frontend/PerformanceTips.html
2016-06-09 15:44:39 +02:00
6d4dd66d1f Generalized code generation for nodes to support more complex control flow in the future.
The basic idea is that a node represents a function F, without assuming anything about
how the input expressions are evaluated:

F = F()

In most common case this function can be decomposed and defined in terms of concrete values,
which are given by the input expressions:

F = F'(G1(), G2(), ...), where Gn are the respective inputs of the node

This is a first *very* naive implementation. It would rely heavily on common-subexpression-
elimination (CSE) to avoid generating the same code paths over and over again. There should
probably be some initial optimization on the BVM node level instead of relying just on LLVM passes.

The purpose of this generalization is to allow more complicated control flow inside node functions.
Nodes would then also be able to take additional variable arguments (e.g. an index), which can
be modified before being used as internal arguments for input expressions.
2016-06-09 12:29:27 +02:00
a6fdd7d876 Merge branch 'master' into object_nodes 2016-06-08 09:41:04 +02:00
95a603c607 Basic Gabor noise implementation for BLI_noise and blenvm.
This is just the very basic version of the sparse convolution noise
with a Gabor kernel. It does not yet have derivatives or anisotropic
filtering. The only frequency spectrum implemented atm is isotropic
band-limited noise.
2016-06-08 09:14:07 +02:00
ee58ba226e Removed cmake macro entry for deleted bf_blenvm_llvm_modules. 2016-06-03 10:24:14 +02:00
570bbf680a Support for image sampling in the LLVM backend.
This reintroduces the "globals" concept for mapping ID datablocks with
a consistent permanent identifier key.
2016-06-02 17:30:33 +02:00
8fda725b18 Fix invalid function signature when passing derivatives for types that don't support them. 2016-06-02 14:58:19 +02:00
de09b75175 Unify code generation through the llvm API.
Also standard functions for copying and zeroing values are now
implemented per type rather than relying on a simple load/store.
This will become more important with larger aggregate types such as matrix44.
2016-06-01 16:36:17 +02:00
318201968f Removed the unused "simple" (without derivatives) compiler implementation.
As long as the compiler class is WIP the two implementations go out of sync too
easily. Better to make a copy of the dual version and then reduce it when the
compiler class is reasonably stable.
2016-05-31 10:33:30 +02:00
926bdb8a05 Removed the WITH_IRMODULES option, used previously for loading IR code from text files.
This approach suffers from difficulty of generating machine-independent IR code
and is better generated using the C++ API directly. The llc tool can be used to suggest
implementations with C++ API usage, see for example
http://fdiv.net/2012/11/16/llvm-generates-code-that-generates-code
2016-05-31 10:09:35 +02:00
37db749fa6 Merge branch 'master' into object_nodes 2016-05-30 16:36:07 +02:00
d8e0006237 Generate names for formal function parameters to make LLVM output more readable.
Note that these names are not identifiers and don't have to be unique, their only
purpose is to aid in understanding the generated LLVM code.
2016-05-29 13:34:02 +02:00
bdd9bff0b3 Replace function signature for dual values with a flattened sequence of basic types.
This avoids the allocation of aggregate struct space for duals and allows better
optimizations.
2016-05-29 11:29:02 +02:00
ded2b507a1 Move the node-output-to-value map into compiler subclasses.
This is in preparation for specializing the value types of compiler implementations,
so that we can replace dual struct types with independent llvm values.
2016-05-28 11:29:01 +02:00
ac7d221734 Rename NodeValue to NodeConstant, to make it more consistent with LLVM naming. 2016-05-28 08:22:11 +02:00
b7bb0a0c4d Cleanup: use argument pointers for aggregate base types. 2016-05-27 17:20:52 +02:00
d8aaed17eb Merge branch 'master' into object_nodes 2016-05-27 16:01:49 +02:00
e6777be1d1 Derivatives for all the basic math node functions. 2016-05-27 12:26:05 +02:00
61b728a199 Small fixes for compiler errors in VS2013. 2016-05-27 11:01:37 +02:00
f40b79378c Use a more consistent naming scheme for value and derivative functions with prefixes. 2016-05-27 09:37:24 +02:00
d5408d7b13 Another depsgraph fix, internal texture relations need to be built explicitly.
Like node trees, textures are only optionally constructed in the depsgraph when something
uses them. When a modifier (or other potential future user of the "handle" system)
declares a texture dependency, the internal relations of that texture are not necessarily built -
this only happens when the texture is part of a material texture stack. So we need to also
construct the internal texture relations (lazy init) in addition to the external modifier->texture
dependency.
2016-05-27 09:06:39 +02:00
2b9d5205e6 Fix for node group updates: need to force-update the group node because of crappy node update system. 2016-05-26 15:17:40 +02:00
ce8c882509 Fix node tree group relations.
Recent commits removed an important depsgraph relation between a node group and its tree.
2016-05-26 14:52:26 +02:00
ae8bde9f09 Fix for pynode group registration.
Because pynode groups now use the bl_idname of tree types rather than
expecting a python class attribute, these tree types need to be registered
*before* generating node group classes. Messy stuff, needs some rewriting.
2016-05-26 09:01:49 +02:00
3960e5671d Fix for depsgraph building of modifiers, missing nodes for ID blocks (textures).
The updateDepsgraph callback for modifiers needs to be used in both passes of
depsgraph building, once for creating nodes and then for relations. Without the
initial node building pass some datablocks like textures can be missing, if they
are not incidentally built by other means such as a render texture.
2016-05-26 08:45:50 +02:00
8478d4127e Use (1,0,0) and (0,1,0) as derivatives of texvec when no explicit derivatives are given.
This is a bit stupid, but allows testing the system without major changes
to old textures or the displacement modifier.
2016-05-25 17:34:46 +02:00
0e30440080 New node "Get Derivative" for using a derivative as the value.
This is mostly a debugging feature for getting visual feedback about
the derivatives. The function doesn't work in the regular scheme of
decomposing a function into the value and derivative parts, so it is
only implemented in the internal IR (making it directly inlineable).
2016-05-25 17:20:50 +02:00
cabcfb0062 Use the common BVM_DEFINE_ALL_OPCODES macro to simplify node->opcode mapping. 2016-05-25 12:10:58 +02:00
09385dcda0 Fix for uninitialized variable and removed some unused code. 2016-05-25 11:58:09 +02:00
f8ef1f0d09 Straightforward texture eval function in BKE for getting texture results with derivatives. 2016-05-25 10:35:37 +02:00
2030217042 Use derivative functions for calculating dual values using the forward accumulation method.
Only a few functions have derivatives defined yet.
2016-05-24 18:14:11 +02:00
3dae50bc4c Cleanup: Remove the unnecessary compiler subclasses for final instantiation. 2016-05-24 12:07:31 +02:00
f19a6f39c5 Cleanup: renamed "codegen" files to "compiler" and split the implementation. 2016-05-24 11:36:16 +02:00
0e8cd49e3b Move IR function implementations using the LLVM api into modules.h. 2016-05-24 10:58:31 +02:00
ee9797808c Generate wrapper functions in LLVM for converting dual types to primitives.
Currently Dual2 arguments are structs with the main value and 2 derivatives
in x and y. Node functions will generally be implemented using the base types,
so the value and derivatives have to be extracted and later combined using
the chain rule.
2016-05-23 15:07:44 +02:00
fd6a3c863f Generate dual values for input constants to match the declared function signatures.
Note that actual functions currently don't use Dual2<> values, but because they
only read/write the 'value' field it works anyway ...
2016-05-20 08:00:38 +02:00
cdda6bd19a Fix module linking: initialize the node graph module right away to avoid repeating code. 2016-05-19 18:34:08 +02:00
d98433c467 Leave the decision about passing arguments by pointer to the compiler class too.
This removes the last "global" type function, so all the details of types are now
decided in the compiler implementation subclass.
2016-05-19 18:22:53 +02:00
8a790c2b86 Removed unused codegen_begin/end functions. 2016-05-19 18:06:56 +02:00
bf007badbf Define a separate node function module for every compiler type.
This means we can have differing implementations for every potential use
of nodes, such as plain value functions vs. dual values with derivatives.

Node functions for each compiler type are stored in a static map and are
lazy-initialized on the first node graph compilation.
2016-05-19 18:04:21 +02:00
d54b4d2532 Pass texture arguments and return values as dual values.
Currently only the value part is actually accessed. Using dual values
allows the node graph to handle input/output arguments are single a
single llvm::Value instance.
2016-05-19 11:52:48 +02:00
030a327f40 Preparatory changes for differing node compiler implementations.
Uses some virtual functions of the node compiler to allow different
implementation of values. This will be used for calculating values
and partial derivatives in texture nodes.
2016-05-18 17:40:55 +02:00
ce022f4506 Use a TypeSpec pointer for structure fields rather than direct storage. 2016-05-18 11:28:50 +02:00
2ade7d5815 Type system refactor: Use a global type definition map instead of defining each type locally. 2016-05-18 08:03:47 +02:00
076564e5bd Merge branch 'master' into object_nodes 2016-05-16 12:45:24 +02:00
82fc0e02ab Cleanup: removed some unused code. 2016-05-16 12:44:25 +02:00
a601d5d18f Enable the new depsgraph by default.
The blenvm system depends on the new depsgraph, so it doesn't make sense
to have to enable it every time.

The legacy depsgraph can still be switched back on by the --enable-legacy-depsgraph option.
2016-05-16 12:37:14 +02:00
97681db89f Internal definition of commonly used node functions, so that LLVM can optimize better.
This currently includes just a few 'value' nodes, but should be extended as much as feasible.
In particular many vector functions can be split component-wise.
2016-05-14 19:49:57 +02:00
be79a6886b Implemented common procedural texture patterns for LLVM. 2016-05-14 12:44:35 +02:00
df2a739a86 Include all module functions in a single header for convenience. 2016-05-14 10:25:56 +02:00
b9ad0b94c0 Simple coordinate input node for texture nodes. 2016-05-14 09:55:20 +02:00
939810add5 Handle all node type kinds appropriately.
The LLVM backend also supports PASS nodes, even though these are usually
removed from the node graph in a preprocessing step for clarity.

ARG nodes have their output values mapped in advance, so no extra code
needs to be generated for them.
2016-05-14 09:53:33 +02:00
2d9bad03b7 Fix incorrect argument order for vector/color constructor functions. 2016-05-14 09:52:38 +02:00
d435274887 Replaced mix of bool type flags for node types with a single enum for special kinds of nodes. 2016-05-14 09:09:05 +02:00
83b1b3417b Implemented math functions for the LLVM backend. 2016-05-13 18:08:02 +02:00
da70902731 Fix for missing notifier application on texture node trees.
The pynodes tree does not have the static NTREE_TEXTURE type set.
2016-05-13 15:48:24 +02:00
9616b72e57 Fix node socket update callbacks exiting early because of missing output connection.
Outputs are currently identified in the nodes via the DO_OUTPUT flag. This is not
nicely accessible for pynodes and needs to be reimplemented in a proper way.
Until then this optimization for node recalculation should just be ignored for texnodes,
so nodes now have a callback for checking usage, and will always be considered used by default.
2016-05-13 15:46:15 +02:00
ca85cb436d Remove redundant dependency declaration from pynodes, which creates a circular dependency.
Previously the "parameters" depsnode was the POST operation for node trees, but has now
been supplanted by the cache invalidation node. Making an explicit dependency on the component
thus creates a cycle.
2016-05-13 11:22:00 +02:00
464505174b Switch texture nodes to a pynodes implementation.
This also removes the static bNodeTree type definition C, because
BVM requires some functions that are best defined in python (and it
would have been switched eventually anyway).
2016-05-13 11:15:54 +02:00
545413d8e8 Removed texture nodes.
The node tree type is left alone for now, only the node types are removed.
2016-05-13 07:53:03 +02:00
0e974d6e91 Define the base opcode functions for LLVM and use util_ files for types. 2016-05-12 19:26:03 +02:00
0e7c9a93ae Split opcode macro into thematic sections to make it easier to handle them in parts. 2016-05-12 18:17:32 +02:00
2d3f526403 More meaningful dependencies between node tree parameters and cache-invalidation operations, and textures that use them. 2016-05-12 17:56:54 +02:00
894511d568 Fix for regenerating functions with MCJIT.
LLVM was reusing old machine code, apparently regenerating it explicitly
with EE->generateCodeForModule() fixes this.
2016-05-12 17:51:54 +02:00
436e84a12c Fix for node function invalidation through the depsgraph.
We have two separate caches for bvm and llvm backend, both of them
might contain a function for the node tree.
2016-05-12 16:50:43 +02:00
50a244951b Merge branch 'master' into object_nodes 2016-05-12 09:56:05 +02:00
f49b698e87 Merge branch 'master' into object_nodes 2016-05-11 11:47:42 +02:00
e23c508f1c Fix the module.cc file. 2016-05-11 10:15:50 +02:00
38c5bf5f63 Small fix for llvm file includes. 2016-05-11 10:14:41 +02:00
c3888a6eaf Silence a few (false positive) compiler warnings about uninitialized variables. 2016-05-11 08:48:54 +02:00
948e0cfea8 Cleanup: removed unused code for struct return values and pointer arguments. 2016-05-10 11:24:33 +02:00
2556262f6c Implemented external C function linking and change of function signature convention.
The general signature layout for node functions is

void foobar(out1_t *out1, out2_t *out2, ..., in1_t in1, in2_t in2, ...);

All outputs are passed as pointers or references (using references in C code for clarity).
Inputs can be passed by-value or as const pointers/references (for structs and arrays).

In the future we may also support single value return types for convenience.
2016-05-10 11:14:50 +02:00
b220c9e5c6 Moved type definition functions for LLVM types into own file. 2016-05-09 09:12:37 +02:00
24b78398cf Disable IR module loading for blenvm by default (with a compile option).
Base modules for nodes will instead be declared as external functions with regular
linkage through C code. This means some potential for low-level optimization might be lost,
but this shouldn't be a big problem as long as node functions don't get too complex
(which means they should be split up in the first place).

IR module loading is not the real issue, but generating the IR code from C/C++ code
in a generic way is not so easy. In the future we may actually generate most code for
basic operators and types directly inside blenvm and only use precompiled modules
for more complex functions.
2016-05-09 08:17:30 +02:00
c5fd1aeff2 Merge branch 'master' into object_nodes 2016-05-08 11:15:48 +02:00
318ffd8e3c Remove the unordered_map cmake monstrosity from subdirectory build files.
Doing this in the top-level cmake file for blenvm is enough.
2016-04-12 10:27:01 +02:00
d42a384296 Extended debug printing modes to the LLVM code.
The LLVM IR code can be dumped either in raw form or optimized.
It is stored as a raw text file.
2016-04-12 10:12:19 +02:00
9edebfb9ab Minor fixes for compiling without LLVM. 2016-04-11 17:21:20 +02:00
103ecf7aca Fix expression result use as function arguments and add RGBA color mix node. 2016-04-11 16:51:45 +02:00
2c16ec47ed Inlining of base node functions in LLVM IR code.
This requires a somewhat hackish stub function to force linking of inlined
functions in the modules, so that clang doesn't just omit them when
producing IR code. Not very nice, but also not directly related to the
actual Blender code.
2016-04-11 09:16:05 +02:00
4828cf2313 Optimization passes for the generated LLVM code. 2016-04-09 15:47:29 +02:00
50410d1ce5 Completed basic expression tree code generation.
The code uses a pointer-based signature convention for node functions,
which helps to avoid issues with type coercion through clang.
In the future we could either find a more reliable way to produce llvm IR
for the nodes, or use node functions as external functions without inlining.
2016-04-08 17:28:42 +02:00
2205d2870d Use annotation attributes on module functions to avoid name mangling issues.
Names in C++ code tend to get mangled by the clang compiler according to the
respective target ABI. This is unfortunate because it makes it difficult to
map functions to nodes by name. For now we use a function attribute in the module
C++ code to pass on the "true" name of the function and map them internally.
2016-04-07 17:54:53 +02:00
3c30c96850 Make LLVM module compilation depend on header files to force recompile when headers change. 2016-04-07 17:53:14 +02:00
d02c7eeacb Generate function calls for every node, using code defined in precompiled modules.
Note that this is currently not quite working: The clang compiler call used
tentatively for generating the LLVM IR code for modules uses a lowering pass to
coerce struct data types (such as float3) according to the target ABI. This should
be avoided and is unnecessary for our purposes, since we stitch together and optimize
these code fragments ourselves. In principle we could avoid using a C compiler altogether
and write this stuff by hand, but the point of using the compiler is to avoid this tedious
work. More info:
http://stackoverflow.com/questions/22776391/why-does-clang-coerce-struct-parameters-to-ints
2016-04-06 17:52:34 +02:00
37477ffa1c Ported over code from previous llvm branch for loading IR modules and linking runtime code. 2016-04-05 17:57:29 +02:00
3346568b01 Initial code for mapping node sockets to llvm values. 2016-04-05 16:58:21 +02:00
fa1f852381 Cleanup: Fixed deprecated docstrings for file location. 2016-04-05 16:32:16 +02:00
1fc5680442 Cleanup: Renamed Value class to NodeValue, to distinguish from llvm::Value easier. 2016-04-05 16:26:38 +02:00
1ddfdfa31d With the proper formal function parameters for node functions they can now actually be called. 2016-04-05 11:37:43 +02:00
6392e8885a Use getFunctionAddress instead of deprecated getFunctionPointer. 2016-04-05 11:30:30 +02:00
baf585cdc5 Retain functions both for storage in the cache as well as keeping the pointer during eval.
Otherwise the function is freed immediately after each use, making the cache pointless.
2016-04-05 11:04:44 +02:00
1761091ce6 Only insert functions into the function cache when use_cache argument is true. 2016-04-05 10:26:59 +02:00
ff6673d8b8 Type generation for LLVM based on node socket types.
Currently every socket stores a complete and unique type definition.
This should be changed to module-level type declarations and referencing by name.
2016-04-05 10:16:57 +02:00
7658c6cf0d Implement bvm internal function caching and spin lock (missing from previous commit). 2016-04-05 09:17:14 +02:00
7a57a4f871 Use function caching in the same way for BVM as for LLVM.
The previous way of acquiring functions from the cache was actually not threadsafe.
To do this efficiently the cache locking, lookup and potential update is now done
inside the API functions to prevent race conditions on the cache.
2016-04-04 19:28:04 +02:00
72fd685f2f Experimental (and very messy) code for JIT compiling and running a simple stub function for texnodes on the LLVM backend. 2016-04-04 17:34:08 +02:00
84407d57c0 Use a _bvm postfix to distinguish the "gen_XXX_function" and "eval" API methods by backend. 2016-04-01 10:22:12 +02:00
9032c2ae96 Generalization of the function cache feature to support both bvm and llvm backends.
This code is not very elegant atm, but it does the job. Have to see how internal code
management of LLVM (modules) plays into this, the function cache then could become
redundant.
2016-04-01 09:23:35 +02:00
a9cdbda687 Partial revert: Use only BVMFunction stub type as a handle for all backends.
API functions are still distinguished by backend.
2016-03-31 16:39:21 +02:00
15684cda57 Use the BVM postfix to distinguish 'Function' in bvm backend from future LLVM functions. 2016-03-31 16:35:36 +02:00
c25ca841fb Fix incorrect stub type name for texnodes. 2016-03-31 16:14:09 +02:00
05cdeb6745 Added llvm_engine files for managing global LLVM initialization and ExecutionEngine. 2016-03-31 13:00:53 +02:00
038e45de62 Rename BVMFunction to BVMFunctionBVM in the C API.
The awkward double shorthand "BVM" has two meanings here:
 * BVM*** is short prefix for "BlenVM" (to be changed later?).
 * ***FunctionBVM means a function in the bvm backend implementation (see FunctionLLVM etc.)

This should be cleaned up later, by chosing a more sensible name for the overall code module.
2016-03-30 17:08:46 +02:00
d8b97405d1 Cleanup: replace namespace bvm with namespace blenvm in comments. 2016-03-30 16:41:36 +02:00
5415e884f6 Own file for function cache API. 2016-03-30 16:22:34 +02:00
09a8d6794b Moved the modules directory to the blenvm base level, so modules can be shared code.
The idea is to use modules code for all the various backends to avoid lots of duplicate code.
This is similar to how the same code is used in Cycles for CPU and CUDA backends.
2016-03-30 15:07:10 +02:00
cf7c185dc1 Added blenvm_llvm submodule for implementing the LLVM backend to the blenvm node system. 2016-03-30 13:48:50 +02:00
07d3c2c317 Support LLVM header includes.
These were not needed by Blender yet, because LLVM is only used indirectly
through dependencies, which use their own build settings for LLVM include paths.
2016-03-30 12:10:39 +02:00
b5a454f6cf Renamed bvm_typedesc to typedesc: It's not actually specific to the bvm backend. 2016-03-30 09:54:45 +02:00
f56be79da6 Restructuring: Remove the 'bvm' prefix from files that are not directly concerned with the bvm backend.
The naming here is a bit confusing: BlenVM is the name used for the overall code module. BVM can be a shorthand
for BlenVM, but is also the name of the backend for SVM-like code interpreter. General purpose code (such as
node graph and util files) should avoid the bvm prefix. BlenVM as the overall name for the code module is kept
for now, but may be replaced at a later point.

Note that currently one bad-level-include from non-bvm to bvm code remains, for the Value class used in nodes.
Values should ultimately be a backend-agnostic class for storing literal values (node inputs).
2016-03-29 18:48:41 +02:00
245d7a08d0 Merge branch 'master' into object_nodes 2016-03-29 16:45:12 +02:00
39af860e6d Cleanup: Moved value class into own file, away from pure type declarations. 2016-03-09 17:25:58 +01:00
94b4a06c53 Merge branch 'master' into object_nodes 2016-03-06 15:48:32 +01:00
a168e65f1b Merge branch 'master' into object_nodes 2016-02-28 16:12:25 +01:00
f232d7a599 Merge branch 'master' into object_nodes 2016-02-08 10:21:41 +01:00
843e0d1f3a Fix for use of TypeDesc.base_type as a plain value rather than a function call. 2016-02-01 12:38:27 +01:00
c98b02495b New buffer type 'Image' for 2-dimensional arrays. 2016-01-30 13:34:15 +01:00
1c5340de2d Removed the 'Iteration' node, the main index is now an implicit variable. 2016-01-30 11:22:32 +01:00
239c864318 Updated codegen for handling variables and expressions.
Note that kernel nodes now don't have explicit 'function' inputs
any more. Any expression input can be a separate block. Eventually
this should be handled by creating true loops, branches, etc. in the
generated code, rather than reading a jump address from the instructions
and passing it to a blackbox method as a "function pointer".

For the time being the kernel nodes simply prepend a jump address before
*every* input, using BVM_JMP_INVALID in case the input node is not in a
separate block.
2016-01-29 14:31:59 +01:00
067dfb3e52 Adapted the node blockify procedure to the new concept of expressions and variables.
The internal bvm nodes now can contain (implicit) cyclic connections, owing to the
connection of variable inputs to local variable outputs of kernel nodes. These link
cycles are never created explicitly by the node parser (pynodes), but are a result
of the semi-functional programming nature of the nodes: each node constitutes a
function of the local variables defined inside a kernel block. When the nodes are first
constructed from the user-level nodes these inputs are left unbound, and are then
resolved backwards as the individual node functions are inlined.
2016-01-29 11:56:15 +01:00
a4108eba7f Redefinition of the meaning of "value type" for sockets (intermediate commit).
The use of "expressions" for node inputs must be generalized to support array-like
data and a level of functional programming. Inputs are now expressions *by default*.

A node can also have "variables" as inputs, which are implicitly defined in the base node
graph and should not be bounded in the first compiler step (i.e. are not defined by
pynodes). Variables instead are defined as outputs of other nodes (currently still called
"kernel" nodes), which would mostly constitute loops over such variables.

The concepts being worked on here are loosely based on those found in the Halide language
http://halide-lang.org/index.html
2016-01-26 11:51:40 +01:00
9cbdfdbdfa Merge branch 'master' into object_nodes 2016-01-24 14:43:59 +01:00
cb96f9d568 Mockup Nodes: Dummy pynodes for smoke and rigidbody simulations.
These have no implementation yet, they are just meant to test concepts.
2016-01-22 10:56:54 +01:00
dd6a0dc910 Basic image sampling node for reading interpolated color values from an Image datablock. 2016-01-19 23:29:51 +01:00
0b2b29ba56 Cleanup: Simplify depsgraph API by using the same depsnode type for each ID.
This means the user might use an invalid component key for generating dependencies,
but the only consequence is that no relation is created.
2016-01-19 16:41:16 +01:00
a31609a6be Two-level use-count system for bvm pointer variables.
The rationale for this change is that copying variables on the bvm stack
will become necessary for compound variables (structs). In order to
efficiently keep pointers on the stack without making deep copies every
time such a variable is copied, the pointer must be a kind of shared_ptr
concept. In addition to this the stack variable itself must also be counted,
so that we have a way of detecting when it "goes out of scope", i.e. all
nodes using it have finished.

The node_counted_ptr is not a full C++ shared_ptr, because the user counting
is handled explicitly by the compiled bvm instructions. Using a std::shared_ptr
would be difficult due to the mixed levels of evaluation (compile-time constants,
storing shared_ptr on the opaque stack), and because of difficult interfacing
with C code (returning persistent plain pointers).
2016-01-18 13:05:36 +01:00
0549e0eeaf Cleanup: use size_t (i.e. size in bytes) for data types, rather than explicit stack size.
The stack size is calculated during codegen by an int-ceil division,
currently padding to int size (probably later to be changed to int4 for
better SSE support).
2016-01-16 17:44:33 +01:00
e44d648779 Cleanup: Use an real type 'EvalStack' for passing the value stack around.
This should avoid possible confusion of values (floats) with the more
abstract value stack.
2016-01-16 17:25:07 +01:00
40150c9e6e Extended TypeDesc to support compound type definitions (structures). 2016-01-15 11:10:03 +01:00
9b22c3854d New util file for structure type implementations in C/C++. 2016-01-15 10:08:09 +01:00
475c13a7d2 Rename: POINTER base type to "RNAPOINTER".
This is so the POINTER type can be used for general struct pointers and arrays.

RNA pointers should eventually be replaced with more specific types for
data that can be accessed in the bvm system.
2016-01-14 15:38:30 +01:00
0df953fb92 Fix for incorrect pointer type in smoke effector updates. 2016-01-14 09:42:31 +01:00
d9d9c97106 Moved util_typedesc file into the compile module. 2016-01-13 19:32:24 +01:00
66070ec814 Split off from the bvm::Function class the basic instruction list functionality.
The point of this is to keep type definitions out of the core bvm eval code.
TypeDesc is only used for the outermost layer of bvm::Function in order to
copy arguments to and results from the eval stack. OpCodes themselves are
agnostic to higher type definitions and just work with raw data.
2016-01-13 19:26:17 +01:00
04f9286c07 TypeDesc registration in NodeGraph for reusable type definitions.
Eventually this could allow actual user typedefs (through the API),
but primarily this is to make complex type definitions possible without
having to do a full definition every time, and simply use the type name.
2016-01-13 14:54:46 +01:00
431b90c969 Merge branch 'master' into object_nodes 2016-01-13 12:19:32 +01:00
326255d080 Fix a memleak of the return value user counter from mesh_ptr.
The returned mesh_ptr result needs to be cleared to ensure the user
counter is also freed. To avoid deleting the mesh data as well a new
function 'reset' was added, which frees the user counter but only resets
the data pointer without freeing it.
2016-01-13 11:02:00 +01:00
0937f5baa3 Another geometry node for loading another objects 'final derived' mesh.
Modifiers should be able to work on meshes directly as much as possible,
without direct reference to an object. The space transform should
ultimately be passed along with the DM (TODO, currently requires explicit
inputs).
2016-01-12 14:48:10 +01:00
29daf7dedb Prepend a value node on all (non-constant) node inputs without a link.
In principle the input value could be used directly in codegen, but this
approach means one less condition to check and has the same effect.
2016-01-12 14:46:33 +01:00
71747fa395 Fix for incorrect output type displace element.index. 2016-01-12 14:39:15 +01:00
af0215918e Added a simple operator and panel for one-click debug display of bvm node visualizations.
This was formerly part of an addon, but is useful enough to include directly in the branch
for debugging purposes. It uses the existing graphviz dump functions to write into a
temp dot file, then runs graphviz on it and opens the resulting image in the standard
system image viewer.

This is tailored for linux systems, but that shouldn't be a big issue since most developers
(who are the target audience) use this platform anyway.
2016-01-12 12:22:15 +01:00
9fccaadb84 Use the new node block structure in code generation, to determine dependencies of symbols. 2016-01-12 11:40:35 +01:00
97642e0c96 More elegant construction of implicit node blocks, by copying argument node first. 2016-01-11 15:24:36 +01:00
f918012866 Represent nested node blocks in the graphviz debug dump. 2016-01-09 17:40:19 +01:00
65b4da0cc3 Cleanup: class for debug dumping functions for node graphs. 2016-01-09 15:01:30 +01:00
d5707b9a05 Moved the block definition from codegen to the node graph optimization.
This now involves making actual copies of nodes, so that each block has
a unique instance of each node and ambiguity is avoided.
2016-01-09 14:07:58 +01:00
c882d15626 Generalized algorithm for constructing node blocks based on local arguments.
Nodes can have expression inputs and override argument nodes with local
variables. This means that a local block must be created for each expression
input. The boundaries of such blocks are defined implicitly by upstream nodes
which define their own local arguments (or ultimately by the graph arguments).

The current version is still not quite generic enough to allow arbitrary
nesting of blocks, but it should ensure that expressions don't include all
the parent nodes which don't depend on local arguments and therefore should
be calculated in the parent block (a form of constant folding).
2016-01-08 17:31:46 +01:00
3598ef2536 Use a global node index assignment to enable sorted sets for nodes.
This simplifies code generation because we can use a set with efficient
lookup which also keeps a valid ordering of nodes as dictated by links.
Since any generated code uses only subsets of nodes, the ordering still
is valid for any BasicBlock.
2016-01-07 12:11:15 +01:00
5eaabb9aac Moved BasicBlock out of the Compiler struct. 2016-01-07 11:11:22 +01:00
8796a997d6 Fix for missing stack indices in expression blocks.
When creating an expression block, the parent block stack indices must
initialize the local index map, so that external variables can be accessed.
2016-01-07 10:41:35 +01:00
a16ff1f568 New node for finding the closest point on a mesh.
This will be useful for shrinkwrap-type modifier behavior.
2016-01-06 13:46:37 +01:00
9121c804a7 Cleanup: Moved some classes out of the typedesc utility file. 2016-01-06 11:29:16 +01:00
6858b1fd81 Fix incorrect header naming. 2016-01-06 11:25:08 +01:00
72878c26a2 Cleanup: Use node/socket pair keys more consistently instead of pointers. 2016-01-04 15:21:14 +01:00
01f7d71fa7 Strict separation of input and output socket keys for clarity.
This avoids confusion regarding what socket keys are stored in maps etc.
2016-01-04 13:47:03 +01:00
0313128207 Merge branch 'master' into object_nodes 2016-01-04 10:48:52 +01:00
289493c74f Added basic semantics to the debug compiler by accessing node types. 2016-01-04 00:39:55 +01:00
1313440c59 Debug representation of generated bvm code.
A specialized compiler variant is added that turns generated code
into a graphviz diagram of the instruction lists.
2016-01-03 23:59:29 +01:00
8b5cae92c5 Abstract virtual base for the BVMCompiler class.
This will allow different implementations of the compiler depending
on the intended target. This could be other bytecode formats (LLVM, SPIR),
but most imminently would be used for debugging.
2016-01-03 12:55:14 +01:00
a7da96ad1b Merge branch 'master' into object_nodes 2016-01-03 10:57:26 +01:00
97e00d6091 Fix for broken pynodes: BVM functions in NodeTree must be optional. 2016-01-03 10:55:30 +01:00
37875dc003 Renamed map of basic blocks used for expressions. 2016-01-01 17:41:41 +01:00
4ca0309f13 Renamed the FUNCTION input value type to EXPRESSION for clarity.
'Function' is a more general term, the input is specifically an
'expression' because it returns a single value.
2016-01-01 17:41:41 +01:00
4c6b05322c Renamed FunctionInfo to BasicBlock, because that's what it is generally called in compilers. 2016-01-01 17:41:41 +01:00
acd4b54f91 Implement more texture types.
This adds support for marble, magic, stucci, distorted noise, musgrave
and wood textures.
2016-01-01 17:27:00 +01:00
8331fceec6 New vector math mode for vector length, patch by Philipp Oeser (lichtwerk). 2016-01-01 13:42:13 +01:00
7335106206 Output values in node graphs are unused, removed.
In fact there is no per-output data in node instances at all, so the
OutputInstance data was removed entirely. All the connectivity and
value information is in the inputs.
2015-12-31 15:05:25 +01:00
08530c7eea Fix memleaks in refcounted data pointers on the bvm stack. 2015-12-31 14:56:54 +01:00
8c5041be5c Fix for memleaks caused by un-freed Value instances, kudos to Kevin Dietrich!
These values are heap-allocated virtual classes, because general node code is
largely agnostic to data types. After adding values to a node (either as
default input values or per instance), care must be taken to free them when
the node type or instance is destroyed.
2015-12-31 13:11:19 +01:00
bb0e1375b1 Merge branch 'master' into object_nodes 2015-12-31 12:04:51 +01:00
8e0489442f Reusable functions for copying a set of nodes and calculating node bounds. 2015-12-29 09:12:40 +01:00
77ae38677c Added an optional update callback for the configurable object node ID property. 2015-12-28 11:27:34 +01:00
e26d7a72fd 'Make Group' operator (ctrl+g) to easily create a node group from selected nodes. 2015-12-27 18:39:48 +01:00
397e1aeccb Use a unified list for simplifying socket identifiers and matching RNA struct types. 2015-12-27 17:17:43 +01:00
48e0785dc5 Fix compiler warning from illegal zero-initialization in C++0x. 2015-12-27 14:42:37 +01:00
f036fa079c ID.flag has been split to put runtime tags in a separate field (rB3fcf535d). 2015-12-27 14:35:06 +01:00
ad072beb68 Merge branch 'master' into object_nodes
Conflicts:
	source/blender/depsgraph/intern/depsgraph_build_nodes.cc
	source/blender/depsgraph/intern/depsgraph_build_relations.cc
2015-12-27 14:21:03 +01:00
898d2ac36c List existing node groups in 'Group' categories for easy instancing.
This includes polling by type and recursion level to ensure groups
are only added in appropriate tree types, and don't create cyclic
dependencies.
2015-12-27 10:29:30 +01:00
36c6cbb902 Poll node groups based on node tree datablocks rather than node instances.
There is a redundancy in the nodetree/node relationship, since each nodes
is part of exactly one node tree. This means we can test for either trees
or nodes to detect cycles, and because we usually poll for node trees
rather than nodes that would be the natural choice.
2015-12-27 07:28:10 +01:00
d3b626711f Automatically create a new node tree when making an object component node. 2015-12-25 18:19:29 +01:00
6a82c17c10 Create a default minimal node setup when creating new trees or groups. 2015-12-25 17:06:21 +01:00
842a138071 Duplicate enum values lead to creating mesh instead of dupli sockets in node groups. 2015-12-23 18:46:04 +01:00
c92dc52f34 Fix for empty dupli list defaults, these must be neither NULL nor static. 2015-12-23 18:34:13 +01:00
e339326621 Use dedicated functions for producing graphviz debug output.
This allows creating unoptimized node graph output dumps without
causing crashes, because the codegen step is not performed when
just producing debug output.
2015-12-23 18:10:22 +01:00
894c9f3f85 Added RNA function for debugging dupli bvm nodes. 2015-12-23 17:35:54 +01:00
a51fc1b993 Fix for node groups: operator for creating a new tree was hard-wired to geometry nodes. 2015-12-23 17:12:09 +01:00
3e2fa696bf Added missing bf_blenvm linking for blenderplayer. 2015-12-23 15:21:35 +01:00
f0860367f1 Use dynamic socket lists for mesh and dupli output nodes for convenience. 2015-12-23 14:45:33 +01:00
a2f5fc297f Generalization of the dynamic socket list feature, currently used by mesh-combine. 2015-12-23 14:38:59 +01:00
70e84dc17a Use default node input value for constants. 2015-12-23 14:37:27 +01:00
92f55bcf87 BVM node for combining dupli lists. 2015-12-23 13:18:45 +01:00
d0949254da Extended notifiers from node value editing to include instancing trees.
The use of notifiers from node trees is very unsatisfactory atm.
Notifiers should be integrated into the depsgraph, so that changes
to data automatically trigger appropriate UI redrawing.
2015-12-23 13:00:29 +01:00
562bcb4d36 Implementation of the "make dupli" node to generate a single dupli object. 2015-12-23 12:46:36 +01:00
aacff41363 Use a std::vector for storing Dupli instances in bvm for efficiency. 2015-12-23 12:24:00 +01:00
1a37890ac0 Return duplis from a bvm node evaluation, just like DerivedMesh. 2015-12-22 14:30:09 +01:00
9af1dfb91d Fix for default values of array types.
Now an array can be passed directly to the add_input functions to
construct an ArrayValue.
2015-12-22 13:55:26 +01:00
9f1165c8aa New opaque type 'DUPLIS' for storing a dupli list.
This works just like the MESH type, but wraps a ListBase instead of
a DerivedMesh. Using this type the duplis can be generated internally
and returned as a result.

All such data types should be handled in a generalized way eventually.
2015-12-22 13:30:17 +01:00
5c9ccf04a4 Correct types for array 'pass' nodes. 2015-12-22 12:40:19 +01:00
13329d9a63 Reordered data types a bit, so mesh and other complex types are at the end. 2015-12-22 12:02:19 +01:00
674bb4b6a4 PASS nodes for array types. 2015-12-22 11:07:41 +01:00
66a41f2e9f Array types for node sockets.
These are an extension of the type description to support contiguous
arrays of base values. They effectively form a second set of types,
mirroring the 'single' base types. Socket connections will have to
strictly match between these types as well, i.e. any dereferencing and
array access will have to be explicit.
2015-12-22 11:07:41 +01:00
ab25e2f6c6 Sqrt math node mode, patch by lichtwerk. 2015-12-21 12:09:28 +01:00
bb96d3d29b Removed unused function declaration. 2015-12-20 11:42:39 +01:00
031c14e123 New dupli mode for use of BVM node trees to generate duplis.
Not yet functional, as it doesn't have opcodes yet.
2015-12-20 10:07:48 +01:00
83ca36a6e9 Object pointer should not be needed for constructing modifier node functions. 2015-12-18 16:27:52 +01:00
9f766ea134 Merge branch 'master' into object_nodes 2015-12-18 13:31:25 +01:00
fe3657a575 Use the bvm function cache for modifier nodes, to avoid recompiling all the time. 2015-12-16 13:43:36 +01:00
5353ae384d Separation of dependencies of node trees and their users for compiling vs. evaluation.
So far any change in a node tree would cause both a recompile of bvm functions
as well as a re-evaluation of any users of that function. This is quite inefficient
and should be separated to allow caching of the bvm functions independent from
their users.

To this end the nodes now have 2 callbacks: one for getting compile deps, the other
for getting eval deps.
* Compile dependencies are typically other node trees (node groups). In the future
  this could also be Text datablocks, similar to how OSL script nodes work.
* Eval dependencies are all the blend_data blocks that a function might access.
  For modifier nodes these could typically be meshes, curves; for shaders could
  be images, textures; and so forth.

Separating these dependencies means the bvm functions will have to be recompiled
only when the actual bNodes change (add/remove, linking, socket values).
Conversely when some modifier input data changes (editing, cfra) the modifier
function will be re-evaluated.
2015-12-16 13:25:46 +01:00
d736d8073b Use C callbacks for implementation of DepsNodeHandle.
This makes it possible to create generic dependency functions to
handle dependencies in various ways, using the same reporting callback.
Currently used for depsgraph building in 2 passes (nodes & relations).
2015-12-16 09:01:20 +01:00
69687b7c5c Removed default case for the opcode switch, all opcodes should be handled explicitly. 2015-12-15 07:58:38 +01:00
3cb01d1409 Merge branch 'master' into object_nodes 2015-12-14 13:58:32 +01:00
caec65686f Explicit depsgraph tagging for object nodetree is not necessary any more. 2015-12-14 13:56:51 +01:00
929324995f Extension of depsgraph API to handle the relations between node trees.
This allows node groups and similar nodes with a node tree ID pointer
to register in the depsgraph.

In addition to defining relations, the builder must also make sure
that the node tree depsnodes are created first, because, unlike objects,
node trees depsnodes have no direct scene connection and are not generated
in advance. Since the build process has two distinct steps for creating
nodes and relations, the same API is used for both to simplify the code.
2015-12-14 12:19:14 +01:00
6b97c36df3 Generalization of function caching to avoid unnecessary recompilation.
The function cache allows callers to store functions in a cache (hash table).
The functions also have a reference count, so replacing the function
in the cache (when the node tree changes) does not immediately delete
the current instance. This allows background threads to keep using the
old function until they can cleanly return (tasks should be restarted
when the nodes become outdated, but this is up to the caller system).
2015-12-14 08:16:35 +01:00
e6bf664d78 New data type for constant (!) strings.
The const-ness is very important: strings are only stored as const char*
on the stack, which point to memory inside the instructions list.
The instructions contain the actual char array, with a null terminator.

This does not allow allocating new strings during eval, but it makes
it possible to use static strings as identifiers and keys for global data.
Eventually we could allow string operations through an allocation system
similar to DerivedMesh handling, with init/release nodes to manage
alloc/free.
2015-12-12 12:40:30 +01:00
465327ff9f Merge branch 'master' into object_nodes 2015-12-12 11:56:39 +01:00
434123a6ea Use the relative transform of target objects and the modified object to adjust modifiers.
This affects modifiers which use an external target object (currently
boolean and curve path). The modified object is a graph input argument,
which the target object is looked up via its key. The relative transform
means the values calculated from external geometry are always in the
modified object's local space.

Note that transform and its inverse are provided explicitly to nodes.
This will allow optimization for constants later on.
2015-12-11 18:30:25 +01:00
9f01d87ddf Merge branch 'master' into object_nodes 2015-12-11 16:24:16 +01:00
6bb58ce842 Boolean modifier node, based on the new bmesh boolean operations. 2015-12-11 16:11:17 +01:00
5dd13527b9 Merge branch 'master' into object_nodes 2015-12-11 10:55:12 +01:00
6f11cf2400 Clamp the curve parameter in the curve path eval, because where_on_path is touchy about it. 2015-12-11 09:46:36 +01:00
e94400084c Reenabled the depsgraph relations for nested node trees, for the time being.
This should also work through the API! However, it requires building the nodetree
depsnodes before making relations which is not well supported yet.
The forced relations are currently needed to make updates inside the component
node trees trigger object recalc.
2015-12-11 09:44:41 +01:00
9f584095d7 Minimal RNA API of the depsgraph for registering object node dependencies.
This simply reflects the existing C API for the depsgraph, as already used
by modifiers. The object nodes gain two callbacks, one for depsgraph
relations and one for registering globals. Since both of these should
require the same objects and other ID blocks to be registered, they share
a common `relations_update` function on the pynode level for convenience.
2015-12-11 09:16:34 +01:00
99e998483b New node "Curve Path" for reading interpolated curve data.
This introduces an improved way of handling ID relations inside the
node tree. Instead of storing simple indices to an object array,
the nodes now generate a 'key' (currently just ID name + lib name).
At eval time this key is then looked up from a hash table in globals,
to produce the actual pointer to the object.

Currently the node will not work, because the curve is not yet registered
in the globals map. This should happen in conjunction with depsgraph
updates.
2015-12-10 20:26:56 +01:00
9d3dc3d57b Merge branch 'master' into object_nodes 2015-12-10 15:21:58 +01:00
b08c193d14 EvalData is now deprecated and unused, removed it. 2015-12-10 15:20:25 +01:00
bed76fe570 Removed global data from the compile process, this should only be needed for eval.
Global data is used primarily for blend_data pointers (objects primarily).
These should not be stored directly inside the function instructions,
but should be stored by a key (hash value) and resolved at eval time.
2015-12-10 15:17:54 +01:00
87cf7d85f5 A number of actual value nodes for simple input constants in the UI. 2015-12-10 14:23:26 +01:00
d5d35d5b15 Moved debugging code output node graph into a utility file. 2015-12-10 12:51:47 +01:00
2e3f780118 Represent local output behavior in the graphviz debug by showing additional links. 2015-12-10 12:37:33 +01:00
e839d2f39a Fix for graph debug: input arguments refer to outputs, can just use the ARG nodes themselves. 2015-12-10 12:01:49 +01:00
2bf4d8f1ae Partial revert: need to allow pointer/mesh value nodes and constants.
These are generated automatically as terminators in pass node chains.
No pointer/mesh values will be stored in instructions though, the
sockets are just dummies to ensure a consistent value node signature.
2015-12-10 11:51:29 +01:00
a30a863a54 Removed 'Value' node types for pointer and mesh data, and disallow hardcoding them as constants.
Pointers in the instruction lists are bound to be invalid most of the time. They should only be
supplied externally via graph input arguments.

Note that opcodes still exist for POINTER/MESH, but these always store a NULL/empty DM
default value, rather than reading from instructions. They are only used by the code generator
as fallbacks if such sockets are not connected.
2015-12-10 11:23:31 +01:00
8432aac5f6 'Value' nodes are actually regular function nodes, not pass-type nodes. 2015-12-10 11:21:51 +01:00
61e5f39eb8 Removed unused code. 2015-12-10 11:13:29 +01:00
5fc8222bab Basic randomization node, producing PRNG values based on either int or float input. 2015-12-10 09:40:50 +01:00
d551b56680 Use a different variable name to prevent shadowing. 2015-12-09 16:29:09 +01:00
61d847f101 Displacement modifier is always expected to affect mesh normals. 2015-12-09 16:27:41 +01:00
8e6177520c Have to ensure correct normals after running modifier nodes. 2015-12-09 16:27:19 +01:00
a50dfeade6 Added procedural Clouds and Voronoi texture nodes to python for general use. 2015-12-09 16:23:00 +01:00
fd2b0e1365 Added vector multiply/divide modes to the vector math node.
In combination with implicit float->vector conversion these can also
be used for simple scalar multiplication. This is not quite ideal
because of the conversion, but math nodes have to be redesigned at
some point anyway.
2015-12-09 15:50:33 +01:00
7b83720817 Some simplifying python functions for copying existing enum properties. 2015-12-09 15:44:14 +01:00
10387fa3ae Added missing draw functions for euler angle nodes. 2015-12-09 15:24:21 +01:00
aa6d62132d New modifier node for generic vertex displacement. 2015-12-09 14:52:31 +01:00
c2722fbc5e Use 'local' output values to override input argument nodes.
This reenables the iteration input used by the array modifier node.
2015-12-09 13:02:30 +01:00
9b1456ba37 Unified handling of both the main function and expression functions. 2015-12-09 11:02:57 +01:00
c7f44335fa Splitting the codegen function into a 'symbol resolve' part and a 'codegen' part.
This basically introduces a linker step which will allow the proper use of function calls.
2015-12-09 10:45:12 +01:00
31e7eb9a13 'Local' value type for outputs to indicate their use as local function arguments. 2015-12-08 21:31:02 +01:00
32becc1813 Use two distinctive structs for node type inputs and outputs.
This helps clarity, and also we need to start using different
value types for inputs and outputs.
2015-12-08 17:26:23 +01:00
be82139b91 Renamed ValueType to InputValueType, because we will need to also have an OutputValueType. 2015-12-08 16:58:28 +01:00
de0933f0aa Replaced EvalData for node evaluation with graph inputs.
Warning: this disabled the iteration input for mesh arrays. To make it
work again the mesh array kernel needs to overwrite the input argument.
2015-12-08 14:50:55 +01:00
306235fa70 Use formal node graph inputs to create formal function arguments and initialize the stack. 2015-12-08 14:21:07 +01:00
079ecfde22 Graph inputs/outputs need an explicit type, since they may not have a node after optimizing. 2015-12-08 12:45:09 +01:00
e493f2039e Keep a list of input arguments of a function in addition to return values. 2015-12-08 12:36:16 +01:00
0b80bb6bb8 Get rid of the extra list of "outputs" for subgraph codegen.
Instead we now pass a socket index map directly to subgraph codegen,
and then read out the final output indices externally.
2015-12-08 12:09:02 +01:00
bc91cba012 Pass input arguments as const void* when calling bvm functions.
This is not used yet, but will replace the EvalData pointer.
2015-12-08 11:11:39 +01:00
00d9beda03 New set of node types for 'arguments' which will act as stack entry placeholders.
These are like value nodes in that an output socket stack entry is reserved for them.
However, like 'pass' nodes they don't actually perform an operation. Their purpose is
to provide a place on the stack for input arguments that other nodes can then read from.
2015-12-08 10:24:16 +01:00
d17a937e89 Expose the get_input function of the node graph just like get_output. 2015-12-08 10:15:20 +01:00
7ec91558f8 Bump the BVM stack size to 4095.
Currently there is no optimization for stack usage, which would ultimately
reduce the required stack size dramatically. 255 is still very low though.
2015-12-07 17:12:58 +01:00
7ef782f1a7 Use individual node types for constructing transform matrices.
Transforms can be created as translations, rotations (euler and axis/angle)
and scale.

The UI nodes include and automatic multiplication node for convenience.
2015-12-07 17:08:36 +01:00
d103f7daab Use an explicit transform input in the array modifier node.
Transforms can only be created from translations atm.
2015-12-07 13:50:03 +01:00
794e16591b Fix for mismatching socket type identifiers.
Have to be careful about distinguishing socket type identifiers
and bvm type identifiers.
2015-12-07 13:49:02 +01:00
3f770bcfeb Fix for ugly socket color because of swapped G and B ... 2015-12-07 13:19:01 +01:00
026ad8d2f9 Merge branch 'master' into object_nodes 2015-12-07 12:37:16 +01:00
17d6b2f158 Some basic math operations for 4x4 matrices. 2015-12-07 12:24:11 +01:00
b985176b57 Moved math eval functions to a dedicated file. 2015-12-07 11:43:38 +01:00
6f99c84123 Introduced a UI node socket type for affine transforms.
This matches the matrix44 internal bvm type. It is useful particulary
for describing rotations.
2015-12-07 09:31:48 +01:00
0665d467ad Fixed and improved the socket conversion function. 2015-12-07 08:47:15 +01:00
02c3eccc31 Cleanup: Renamed socket type utility functions for clarity. 2015-12-06 17:59:41 +01:00
063b5351df Moved the socket converter function into the socket_type module. 2015-12-06 17:25:56 +01:00
d67aab6467 Fix for re-entrant update problems, using a base class with a context manager. 2015-12-06 15:23:52 +01:00
37ac434577 Make node groups work by linking proxy nodes to the external sockets of the parent node graph. 2015-12-06 13:43:32 +01:00
b3bfc8915e Leave the creation of proxies for bNode inputs/outputs to the node compiler. 2015-12-06 13:01:32 +01:00
c6e593d269 New utility module for socket types. 2015-12-06 12:55:10 +01:00
37b980565a Node compiler function for adding proxy nodes. 2015-12-06 12:49:16 +01:00
36e09d2a4e Generalize the node compiler functions a bit to prepare for node groups. 2015-12-06 12:10:37 +01:00
084265454e Moved type conversion utility functions to the common_node module. 2015-12-06 11:37:43 +01:00
19d44991f8 Use update callbacks to sync sockets of group input/output nodes to the interface items. 2015-12-06 11:26:07 +01:00
7c421555bf Use a general update function to update all the nodes that are affected by a group interface.
This is not particularly nice, because it relies on every interface variable to call the
update explicitly. Eventually such things should work via the depsgraph, but this is
currently not easy to do.
2015-12-05 17:14:13 +01:00
57b63e66b5 Register node trees only after creating associated group types.
That way the node group definition can update the node tree type before.
2015-12-05 16:44:02 +01:00
a4ab2d71d6 Added lists of inputs and outputs to node trees for defining the group interface. 2015-12-05 16:09:23 +01:00
6b55e29baf Rudimentary node group classes for an instance node and inputs/outputs.
The node group instance prevents recursive groups by disallowing nested
node trees, both when selecting a different group tree for an existing
node instance, and when adding an existing node to a tree (copy/paste).
2015-12-05 11:32:34 +01:00
0b6ddb5112 Fix for keymap quirk not resetting default property values.
Keymap entries have to use explicit values for properties if they
are modified in any of the variants. Otherwise the value is not
reset to the default in subsequent calls once it gets changed.
2015-12-05 11:29:41 +01:00
10157551be Object node id property poll function should be a regular function rather that a classmethod. 2015-12-05 11:27:59 +01:00
051706bf95 Restrict the ObjectNodeEdit operator to only work on the object nodes.
We want to keep this separate from shader/compo/tex nodes which have
their own node group operators.
2015-12-05 08:36:30 +01:00
f57cfa892f Reorganize python scripts for object nodes into their own script directory. 2015-12-04 18:08:11 +01:00
31d0960734 Merge branch 'master' into object_nodes
Conflicts:
	source/blender/makesrna/intern/rna_nodetree.c
2015-12-04 17:17:52 +01:00
2bac0260a6 Removed useless pass statement. 2015-12-04 13:42:32 +01:00
15689a42fb Better implementation of socket updates for list-style node.
The previous implementation was based on recreating links and did not work
very well when reordering connections, because the insert_link callback does
not allow changing the link target.
2015-12-04 13:35:20 +01:00
3d9fc9016e List behavior for the mesh-combine node.
This is primarily a python implementation for UI convenience.
It makes the list of input sockets behave dynamically for typical list operations:
* adding an element at the end
* inserting an element
* removing an element

Each of these keeps the list compact and shifts remaining sockets, so users don't
have to move each individual connection every time.

Internally it still uses the simple binary combine operation, adding as many
nodes as necessary.
2015-12-04 11:39:36 +01:00
22b2698243 Merge branch 'master' into object_nodes 2015-12-04 10:26:16 +01:00
b475180a57 Merge branch 'master' into object_nodes 2015-12-04 09:20:30 +01:00
8b5af7f3ca Disable explicit update calls _while_ the modal node link operator is running.
These update calls can have bad consequences such as removing node sockets,
which should not happen until after the operator is finished. This was already
questionable in the past. At some point all update handling for nodes should go
through the depsgraph rather than the current fragile update flag/callback system.
2015-12-03 15:33:30 +01:00
fb1ddcc7c1 Merge branch 'master' into object_nodes 2015-12-03 13:04:54 +01:00
48ee73d500 Simple node for concatenating 2 meshes into one. 2015-12-02 17:41:12 +01:00
3675a0fedf Ignore nodes that don't have the 'compile' method, to allow frames. 2015-12-02 17:40:24 +01:00
5ab6b6bbac Added back automatic type conversion.
Now automatic type conversion for sockets happens in the python code.
2015-12-02 13:05:25 +01:00
343c1e3006 Fix for refcounting in the mesh value node.
This node was overwriting the entire stack pointer (incl. the refcount)
but should only store the DM itself.
2015-12-02 12:04:29 +01:00
fe2c73db61 Make sure invalid node connections are rejected at the basic level.
The internal bvm nodes require strict type matching. Any implicit
automatic type conversions should be added on the level of DNA->bvm
conversion. This keeps the bvm nodes simple, and also allows for
semantics on the DNA node level that could not be properly implemented
in bvm.
2015-12-02 11:48:39 +01:00
120ea1179e Improved python API for constructing the internal node graph.
Socket connections can now be handled consistently through single py objects,
so the compilation code becomes more concise and readable.
2015-12-02 11:29:44 +01:00
3f71a178e1 Fix for dangling root sockets when skipping 'pass' nodes.
This can happen when proxies are created, and should always create
a 'value' node to act as a clean root terminator.
2015-12-02 11:28:35 +01:00
d53946ad7e Improved RNA API for bvm node graphs.
Now there are actual collections of inputs/outputs to perform proper checks
on valid names/indices and types.
2015-12-01 18:16:35 +01:00
723c51313c Sanity check for array count <= 0, otherwise CDDM keeps spewing warnings about empty mesh. 2015-12-01 11:04:24 +01:00
e431c5bde6 Added an "Iteration" node as a general-purpose input for expressions.
This is used by the Array modifier node to give a meaningful per-instance
input to calculate a transform from.
2015-12-01 09:52:07 +01:00
91a0b2a8ea Use the jump address in the array modifier to evaluate a transform function per copy. 2015-12-01 08:52:45 +01:00
d125723719 Codegen: for function inputs store the entry point in the instructions. 2015-11-30 16:16:45 +01:00
962478523e Generate instructions for a subgraph in a separate codegen function.
This will be useful for separately generating kernel functions, which
are to be stored as own code blocks so kernels can call them repeatedly
as needed.
2015-11-28 17:07:11 +01:00
dbd3d6d55c Fix for API changes in pynodes. 2015-11-26 14:12:50 +01:00
276b533ffe Fix for stack index assignment to graph outputs. 2015-11-26 14:12:30 +01:00
6db9c275f3 Use a simple value node for graph outputs that are only connected to pass nodes. 2015-11-26 12:47:12 +01:00
11c34a0934 Changing graph output mechanism to use proxy nodes. 2015-11-26 11:13:11 +01:00
1254009072 Distinguish 'function' and 'kernel' node types.
Only kernel nodes will be allowed to have function inputs. This is
necessary because any kernel node will define the boundaries of
node subtrees used for function inputs.
2015-11-25 13:55:20 +01:00
803f7f208c Node inputs now have a 'value type' to specify how input connections are treated.
VALUE_CONSTANT inputs can not have connections, their values are known at compile time.
bvm stores these values directly in the instruction list.

VALUE_VARIABLE inputs store values at evaluation time. bvm stores a stack index for them
which is used to load and store from/to the evaluation stack.

VALUE_FUNCTION inputs are passed to kernels to be called internally when needed.
bvm stores an instruction index and allows a jump to this instruction (TODO).
2015-11-25 12:29:39 +01:00
60779ad90e Added an 'entry point' instruction number to functions.
This is currently always 0, but will be used in the future to allow
storing per-element functions along with the main code. These can then
be called within kernels.
2015-11-25 11:27:37 +01:00
9d0ffa84ce Enabled generic math nodes for modifier node trees. 2015-11-25 11:12:05 +01:00
458ba77ff7 Fix for static empty mesh variable, used as default value of meshes on the stack.
This creates a (minor) mem leak when statically creating the mesh. Instead it
should be constructed in a init/free pair of functions.

Because this variable is used in all different compile units, each of them has
to create their own global 'empty_mesh' variable.
2015-11-25 10:51:28 +01:00
ca6f1efe5d Fixed missing delete call for unused nodes. 2015-11-25 10:01:36 +01:00
527f7d5088 Free the modifier bvm function after use.
Note that eventually these should be cached to avoid unnecessary
recompilation, but for now they get compiled on every modifier eval.
2015-11-25 09:58:10 +01:00
d54eca15d6 Use the guardedalloc new/delete overloads for relevant classes. 2015-11-25 09:53:01 +01:00
044cb7eaf0 Fixed overzealous assert for input stack assignments in codegen.
For constant inputs no stack index is needed. Previous to node pruning
this did not trigger the assert because there were still pass nodes
allocating the stack indices.
2015-11-25 09:29:39 +01:00
170f158b80 Node tree pruning function for removing unused nodes.
This also removes the placeholder 'pass' nodes, which were already
being skipped and made unused.
2015-11-25 09:28:48 +01:00
df9f633da2 Changed node instance storage in the node graph to pointers.
This makes it much easier to optimize the graph by removing and reconnecting nodes.
2015-11-25 09:09:30 +01:00
124bc03035 Merge branch 'master' into object_nodes
Conflicts:
	source/blender/blenkernel/intern/softbody.c
2015-11-24 09:40:33 +01:00
8c17d8e462 Use the base mesh as a fallback result in case no geometry nodes exist. 2015-11-23 12:50:18 +01:00
d4e67dfe37 Fix for store/load of mesh_ptr on the stack.
Most actual mesh operations should only store the data pointer itself
but not modify the refcount.
2015-11-23 12:29:18 +01:00
591e485054 Fix for wrong index in codegen.
Was using the socket instance map sizes rather than the overall number
of sockets of the node type.
2015-11-23 11:42:11 +01:00
71402ca5d9 Added a matrix compose/decompose node for loc/rot/scale. 2015-11-23 11:07:07 +01:00
b1c8112980 Horrible fix for depsgraph/notifier updates through property changes in geometry nodes.
This is really awful, but we don't have a good generic way of defining node tree relations
in the dependency graph yet. More importantly, the notifier system is not up to the task -
putting the responsibility for notifying into operators requires anticipating their effects,
which is not good at all for node operators which affect geometry through complex processing.
The depsgraph really should be handling this.
2015-11-23 10:14:53 +01:00
59080a1d81 Implementation of a simple 'Array' modifier node. 2015-11-22 15:24:07 +01:00
66bb6de449 Fix for invalid comparison operator of SocketPair. 2015-11-22 11:18:35 +01:00
f375f6e6dc Notifier from node changes to redraw the viewport when geometry nodes are updated.
This is a hack! The nodes are defined in python and the notifier is currently
triggered far to broadly, but currently there is no nicer way of doing it.
2015-11-22 10:05:25 +01:00
148ac78b4a Extension of the new dependency graph relations to support object nodes.
This generally triggers a mesh data recalc when *anything* inside the
object node tree is changed. Eventually it should be more specific to
changes in a geometry node tree, but the current design of deps nodes
does not reflect such internal node semantics nicely (although new
depsgraph will allow us to do it properly in the future).
2015-11-22 10:03:01 +01:00
5956bed117 Revert "Fix for unfreed memory of the default empty mesh."
This reverts commit 0ab9e4848d.
2015-11-21 17:20:23 +01:00
0ab9e4848d Fix for unfreed memory of the default empty mesh.
When using a static variable for this it needs to be destroyed explicitly.
2015-11-21 17:00:27 +01:00
6fb22ddfed Reference counting for mesh_ptr variables on the stack.
Any extensive data stored as a pointer on the stack should be ref-counted,
so that it stays alive only as long as used by some node.

The way this works is by enclosing instructions that access the stack
entry with 'init'/'release' opcodes. These are added automatically based
on output socket types (nodes don't have to be created explicitly for them).
2015-11-21 11:56:01 +01:00
9188ac0896 Merge branch 'master' into object_nodes 2015-11-21 11:35:30 +01:00
cc31a5696d Extended graphviz debug printing to also show the graph input/output connections. 2015-11-20 17:37:02 +01:00
1074abc470 Node graph optimization: skip over 'pass' nodes.
These nodes don't have opcodes any more, they are merely placeholders
to simplify node graph construction. Ultimately they will be removed
in the 'finalize' function, along with any other unreachable nodes.
2015-11-20 17:01:43 +01:00
6ee2d69201 Removed unused function declaration get_node_type_from_opcode 2015-11-20 16:03:58 +01:00
5d99ad37ea Use a single geometry subtree also for the graphviz debug function. 2015-11-20 10:22:41 +01:00
4aaa1bed74 Use the geometry subtree in object nodes rather than the toplevel node tree.
This is currently a hack, since only a single geotree is used. Eventually
all geometry subtree result should just be combined.
2015-11-20 10:12:35 +01:00
73bbfe5711 Removed unused code. 2015-11-20 09:58:33 +01:00
da90b8ef79 Added a 'PASS_MESH' node. 2015-11-19 17:19:43 +01:00
07a30be179 Make an extra copy of the resulting node DM after eval.
This is necessary unfortunately because of the unreliable nature of dm->needsFree.
Without it the BKE_object_free_derived_caches function can destroy our static
empty DM at any point.
2015-11-19 17:18:11 +01:00
79dcd08a8a Added an assertion to make sure we don't add invalid node instances with unknown types. 2015-11-19 17:01:46 +01:00
a30be36030 Added a graphviz debug dump function for the object modifier node tree. 2015-11-19 16:42:03 +01:00
8a4fbdcbb4 Use the default output value for graph returns in case they remain unlinked.
In case of meshes this will always be an empty mesh, but it at least gives a valid return value.
2015-11-19 16:12:36 +01:00
ceb9eba6bd Foundations for a mesh data type in bvm 2015-11-19 15:50:36 +01:00
950e2644e8 Mesh type for storing DerivedMesh as a temporary storage on the node eval stack. 2015-11-19 09:59:30 +01:00
a7bfdb0cdf Use a full TypeDesc in node sockets.
For the time being this is synonymous with the base type, but in future
it could allow (fixed-size) arrays and vector semantics.
2015-11-18 10:21:16 +01:00
0c2cfb44f0 Set of basic node classes for representing modifiers in pynodes. 2015-11-18 10:20:49 +01:00
5de05567f9 Simple integration of object nodes into the modifier stack evaluation. 2015-11-18 10:20:19 +01:00
88dcc8566c Merge branch 'master' into object_nodes 2015-11-18 09:27:08 +01:00
6d9406453a Removed an unconventional typename use that causes problems with MSVC. 2015-11-10 10:59:59 +01:00
1e84d69d72 Refactor: renamed 'Expression' to 'Function'. 2015-11-09 16:16:42 +01:00
fbdaaa72e9 Removed unused deprecated code parts. 2015-11-09 13:22:01 +01:00
dec5472349 Disabled some unused function args. 2015-11-08 12:20:26 +01:00
f37d7ed83b Merge branch 'master' into object_nodes 2015-11-08 09:35:20 +01:00
238e2389c0 Add support for texture node trees in new dependency graph. 2015-11-07 17:15:28 +01:00
b03095919a Added clouds noise pattern node. 2015-11-06 17:04:41 +01:00
1d67149c59 Use the material blending function for the RGB mix node for the time being. 2015-11-06 11:47:45 +01:00
d3bc6dcb67 Added the RGB mix node (unfinished). 2015-11-05 17:56:55 +01:00
151ed70fc0 Fix for debug output to avoid backward links when inputs/outputs share a name. 2015-11-05 11:09:41 +01:00
7e50fc041d Added some more opcodes for scalar multiplication and division. 2015-11-04 17:39:20 +01:00
2330dae3ef Added optional automatic datatype conversion for nodes.
This only applies to a handful of common types that can be unambiguously converted.
In any other case the link is considered invalid (proper error handling still needed).
2015-11-04 14:08:59 +01:00
06c957fc70 Made node name arguments optional.
Node names are only used for identifying them in debugging, but
can just be autogenerated as well.
2015-11-04 13:43:39 +01:00
c64c3e41e3 Make the compose/decompose and scalar math nodes work for textures. 2015-11-04 11:39:59 +01:00
84ab551f35 Missing C header for the vfprintf function. 2015-11-04 11:22:33 +01:00
255edd977f Merge branch 'master' into object_nodes 2015-11-04 11:05:49 +01:00
fd1a206f92 Added explicit vector input socket to the procedural texture nodes. 2015-11-04 11:03:02 +01:00
498ca41b8a Added graphviz debug output for parsed bvm node graphs. 2015-11-03 13:51:32 +01:00
6bb619039b Cleanup for the texture node compiler API functions. 2015-11-03 11:02:51 +01:00
965cae5a7b Removed unused code. 2015-11-02 14:27:22 +01:00
3561b4ca36 Use an object pointer from the evaluation data, rather than storing just indices. 2015-11-02 14:23:44 +01:00
09f875e164 Pointer type for passing opaque DNA references and the like through the VM. 2015-11-02 12:44:12 +01:00
e98a35220d Moved the conversion of bNodeTree (DNA/pynodes) to python code for simplicity.
This has several advantages over doing the same in C/C++ code:

- The two parts of the code are clearly separate (parsing DNA settings vs. building and evaluating)
- UI constructs with modes, special cases, etc. can be done in py, heavy lifting still happens in C
- Scripters can easily add own nodes without having to deal with internals
2015-11-01 12:41:49 +01:00
1591616e37 Added first procedural texture node (voronoi). 2015-10-30 10:23:32 +01:00
175e041ddf Texture coordinates node. 2015-10-29 12:31:06 +01:00
846a7c7467 Invalidate the texture bvm program cache when the Tex datablock is updated. 2015-10-29 12:13:07 +01:00
d86fae6d84 Start of a texture node replacement using the bvm system. 2015-10-29 11:19:44 +01:00
a067dccf65 Staged construction of node graphs from bNodeTrees through a "parser".
This wraps mapping of sockets and links in a nice small API.
2015-10-28 17:52:04 +01:00
3692460b5c Added a float4 type for rgba colors mostly. 2015-10-27 12:32:34 +01:00
e0f6b915e8 Internal support for 4x4 matrices in the bvm types. 2015-10-27 10:16:23 +01:00
e5bb9709e6 Renamed 'effector' data to 'point' data. 2015-10-26 13:08:16 +01:00
73de04b202 More vector math node modes. 2015-10-26 12:22:31 +01:00
e3ea07adc4 Effector closest-point node for mesh-based for fields. 2015-10-26 11:32:54 +01:00
2394fe7fbf Merge branch 'master' into object_nodes 2015-10-26 09:34:42 +01:00
5f70cc6c8d Added globals as a distinct argument to the eval function. 2015-10-22 22:52:10 +02:00
bd9f26871e Support for constants as internal node socket inputs.
Constants should never be linked to other nodes (will be ignored).
2015-10-22 14:14:04 +02:00
b229fac8a0 Added an integer type for node sockets. 2015-10-22 13:45:35 +02:00
c9803d3a29 Cleanup: removed unused test code. 2015-10-21 17:24:04 +02:00
abb8eba2ab Fix for multiplicity of internal socket mapping.
A Blender bNode input socket can represent multiple internal node inputs,
so mapping for links must use a set rather than a single socket.
2015-10-21 13:00:38 +02:00
a9f47080d8 First input node: getting basic properties of the "effected" point. 2015-10-21 11:47:26 +02:00
f9f2acfce1 Complementary separate/combine nodes for vectors. 2015-10-21 10:33:42 +02:00
b6e821ee72 Added all the scalar math modes. 2015-10-20 13:57:52 +02:00
76890d8285 Cleanup: Separate folders for compiling nodes and for executing the resulting bvm programs. 2015-10-20 10:46:10 +02:00
8ea69ffbc7 Use a map to ensure proper links between internal nodes.
Socket names usually don't match the internal nodes exactly.
2015-10-19 18:11:29 +02:00
d47b643874 Added a topological sorting pass to make sure nodes are compiled in the right order. 2015-10-19 16:38:05 +02:00
6a890c568e Some simple math nodes to test node connections. 2015-10-19 16:18:05 +02:00
0d751648f8 Test of node conversion from bNodes to internal instruction graph. 2015-10-19 12:16:48 +02:00
e1a6f41fc9 Next compiler step: convert internal node graph to opcode instructions. 2015-10-19 11:17:49 +02:00
e20e6e49ef init/free functions for blenvm to have a place for global initializations. 2015-10-18 16:45:48 +02:00
18815cfaaf First test of the VM evaluation function. 2015-10-18 15:48:09 +02:00
522295f4f7 Create expression return values based on node tree outputs. 2015-10-11 15:02:47 +02:00
b8a4646856 Invoke the BVM expression eval for effectors. 2015-10-11 12:24:55 +02:00
4a239bc2ff Integration of BVM into the effector system for using programmable expressions there. 2015-10-10 10:48:59 +02:00
ec314ae15f Use a dedicated struct EffectorContext for storing results of effector
lookup rather than a plain ListBase.

This paves the way for future inclusion of evaluation context info in
the EffectorContext struct, and hides some of the annoying details of
the ListBase double dereferencing.
2015-10-10 10:26:18 +02:00
bbb5ed35b1 Function for converting a bNodeTree into the internal low-level bvm node graph. 2015-10-09 15:33:08 +02:00
80b6a5e29c Merge branch 'master' into object_nodes 2015-10-09 15:04:09 +02:00
5a5dcc509d Copied the nodegraph classes from the llvm test branch. 2015-10-08 15:11:16 +02:00
f99359418c Added "expressions" as a reduced definition of a function for evaluating instruction lists. 2015-10-08 10:36:30 +02:00
84485606a7 Merge branch 'master' into object_nodes 2015-09-19 08:51:48 +02:00
0590430509 Fix minor error from strict pointer casts. 2015-09-17 10:12:27 +02:00
a358a5734f API for adding and deleting BVM functions. 2015-09-11 14:26:09 +02:00
849b5aec20 New bf library 'BlenVM' ("Blender Virtual Machine", BVM), as a general purpose runtime node backend.
This library provides a way to register modules and functions. Functions are simple
opcode-based descriptions of functionality (programs). They can be evaluated with
a simple interpreter using monolithic kernels, or could be compiled into more
optimized languages via LLVM or OpenCL.
2015-09-11 13:53:34 +02:00
bb5da70b5d Fixed categories for nodes, so only the appropriate nodes for each tree
type are used.
2015-09-11 10:27:28 +02:00
a565324f15 Use the familiar tab/ctrl+tab keymap for opening/closing nodes. 2015-09-11 10:27:28 +02:00
80a149c1eb Some basic nodes for nesting force fields inside the object nodes. 2015-09-11 10:27:28 +02:00
d2442ac1cf Registering an id property type for ObjectNode subclasses is optional. 2015-09-11 10:27:28 +02:00
347e2152db Added an optional poll function for the generic id property of
ObjectNodes.

This allows more detailed polling of ID blocks, beside the basic type.
2015-09-11 10:27:28 +02:00
3694a7bdfc Fix for registering RNA classes with registered parent functions.
The recursive registration for functions uses a simple int array for
flagging found functions. This is shared with the parent, so any child
functions will overwrite parent function flags for the same index
(this is not overloading).

The patch now requires using a larger array that can accomodate all
registerable functions from parents and children. It puts flags for
parents first, then children.
2015-09-11 10:26:06 +02:00
d868990224 A new base type for object nodes.
This includes a generic 'id' pointer for a single ID datablock pointer
in the node. The ID type can be defined using the `bl_id_property_type`
registerable attribute.
2015-09-11 10:25:49 +02:00
4cc10122f2 Context function for getting the object nodes in the node editor. 2015-09-11 10:25:38 +02:00
0cd3fef301 A basic Object node tree pointer property for use as component nodes. 2015-09-11 10:24:59 +02:00
229 changed files with 24505 additions and 4346 deletions

View File

@@ -57,6 +57,13 @@ if(NOT LLVM_LIBPATH)
set(LLVM_LIBPATH ${LLVM_LIBPATH} CACHE PATH "Path to the LLVM library path")
mark_as_advanced(LLVM_LIBPATH)
endif()
if(NOT LLVM_INCLUDE_DIRS)
execute_process(COMMAND ${LLVM_CONFIG} --includedir
OUTPUT_VARIABLE LLVM_INCLUDE_DIRS
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(LLVM_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS} CACHE PATH "Path to the LLVM include directory")
mark_as_advanced(LLVM_INCLUDE_DIRS)
endif()
if(LLVM_STATIC)
find_library(LLVM_LIBRARY
@@ -80,6 +87,9 @@ if(LLVM_LIBRARY AND LLVM_ROOT_DIR AND LLVM_LIBPATH)
OUTPUT_VARIABLE LLVM_LIBRARY
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REPLACE " " ";" LLVM_LIBRARY "${LLVM_LIBRARY}")
# libterminfo is missing in llvm-config --libfiles
set(LLVM_LIBRARY ${LLVM_LIBRARY} tinfo)
endif()
endif()

View File

@@ -601,6 +601,9 @@ function(SETUP_BLENDER_SORTED_LIBS)
bf_gpu
bf_blenloader
bf_blenkernel
bf_blenvm
bf_blenvm_compile
bf_blenvm_bvm
bf_physics
bf_nodes
bf_rna
@@ -749,6 +752,10 @@ function(SETUP_BLENDER_SORTED_LIBS)
list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb)
endif()
if(WITH_LLVM)
list_insert_after(BLENDER_SORTED_LIBS "bf_blenvm" "bf_blenvm_llvm")
endif()
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
foreach(SEARCHLIB ${BLENDER_LINK_LIBS})

View File

@@ -69,7 +69,7 @@ import sys as _sys
import addon_utils as _addon_utils
_user_preferences = _bpy.context.user_preferences
_script_module_dirs = "startup", "modules"
_script_module_dirs = "startup", "modules", "nodes"
def _test_import(module_name, loaded_modules):

View File

@@ -849,3 +849,17 @@ class TextureNode(NodeInternal):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'TextureNodeTree'
class ObjectNode(NodeInternal):
__slots__ = ()
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'ObjectNodeTree'
def bl_id_property_poll(self, id_data):
return True
def bl_id_property_update(self, context):
pass

View File

@@ -0,0 +1,50 @@
# ##### 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 compliant>
if "bpy" in locals():
from importlib import reload
for val in _modules_loaded.values():
reload(val)
del reload
_modules = [
"bvm_debug",
"common_nodes",
"group_nodes",
"forcefield_nodes",
"geometry_nodes",
"rigidbody_nodes",
"smokesim_nodes",
"object_nodes",
]
import bpy
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = {name: _namespace[name] for name in _modules if name != "bpy"}
del _namespace
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,123 @@
### 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 compliant>
import os, subprocess
import bpy
from bpy.types import Operator, Panel
from bpy.props import EnumProperty
txtfile = '/tmp/bvm_nodes.txt'
dotfile = '/tmp/bvm_nodes.dot'
imgfile = '/tmp/bvm_nodes.svg'
dotformat = 'svg'
def enum_property_copy(prop):
items = [(i.identifier, i.name, i.description, i.icon, i.value) for i in prop.enum_items]
return EnumProperty(name=prop.name,
description=prop.description,
default=prop.default,
items=items)
class BVMNodeGraphvizOperator(Operator):
bl_idname = "node.bvm_graphviz_show"
bl_label = "Show Debug Graphviz Nodes"
bl_options = {'REGISTER', 'UNDO'}
function_type = enum_property_copy(bpy.types.NodeTree.bl_rna.functions['bvm_debug_graphviz'].parameters['function_type'])
debug_mode = enum_property_copy(bpy.types.NodeTree.bl_rna.functions['bvm_debug_graphviz'].parameters['debug_mode'])
def execute(self, context):
if not hasattr(context, "debug_nodetree"):
return {'CANCELLED'}
ntree = context.debug_nodetree
if (self.debug_mode in {'NODES', 'NODES_UNOPTIMIZED', 'BVM_CODE'}):
ntree.bvm_debug_graphviz(dotfile, self.function_type, self.debug_mode, label=ntree.name)
process = subprocess.Popen(['dot', '-T'+dotformat, '-o', imgfile, dotfile])
process.wait()
subprocess.Popen(['xdg-open', imgfile])
else:
ntree.bvm_debug_graphviz(txtfile, self.function_type, self.debug_mode, label=ntree.name)
subprocess.Popen(['xdg-open', txtfile])
return {'FINISHED'}
def draw_depshow_op(layout, ntree):
if isinstance(ntree, bpy.types.GeometryNodeTree):
funtype = 'GEOMETRY'
elif isinstance(ntree, bpy.types.InstancingNodeTree):
funtype = 'INSTANCING'
elif isinstance(ntree, bpy.types.TextureNodeTree):
funtype = 'TEXTURE'
elif isinstance(ntree, bpy.types.ForceFieldNodeTree):
funtype = 'FORCEFIELD'
else:
return
layout.context_pointer_set("debug_nodetree", ntree)
col = layout.column(align=True)
props = col.operator(BVMNodeGraphvizOperator.bl_idname, text="Nodes")
props.function_type = funtype
props.debug_mode = 'NODES'
props = col.operator(BVMNodeGraphvizOperator.bl_idname, text="Nodes (unoptimized)")
props.function_type = funtype
props.debug_mode = 'NODES_UNOPTIMIZED'
props = col.operator(BVMNodeGraphvizOperator.bl_idname, text="BVM Code")
props.function_type = funtype
props.debug_mode = 'BVM_CODE'
props = col.operator(BVMNodeGraphvizOperator.bl_idname, text="LLVM Code")
props.function_type = funtype
props.debug_mode = 'LLVM_CODE'
props = col.operator(BVMNodeGraphvizOperator.bl_idname, text="LLVM Code (unoptimized)")
props.function_type = funtype
props.debug_mode = 'LLVM_CODE_UNOPTIMIZED'
class BVMNodeGraphvizPanel(Panel):
bl_idname = "node.bvm_graphviz_panel"
bl_label = "Debug Nodes"
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
@classmethod
def poll(cls, context):
# XXX should use a bpy.app.debug_*** option to enable this
if context.space_data.edit_tree is None:
return False
return True
def draw(self, context):
space = context.space_data
ntree = space.edit_tree
layout = self.layout
draw_depshow_op(layout, ntree)
def register():
bpy.utils.register_class(BVMNodeGraphvizOperator)
bpy.utils.register_class(BVMNodeGraphvizPanel)
def unregister():
bpy.utils.unregister_class(BVMNodeGraphvizPanel)
bpy.utils.unregister_class(BVMNodeGraphvizOperator)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, ObjectNode, NodeTree, Node, NodeSocket
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class ForceFieldNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'ForceFieldNodeTree'
###############################################################################
class ForceFieldNodeTree(NodeTreeBase, NodeTree):
'''Force field nodes'''
bl_idname = 'ForceFieldNodeTree'
bl_label = 'Force Field Nodes'
bl_icon = 'FORCE_FORCE'
# does not show up in the editor header
@classmethod
def poll(cls, context):
return False
def init_default(self):
out = self.nodes.new(ForceOutputNode.bl_idname)
out.location = (100, 20)
class ForceNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'ForceFieldNodeTree'
class ForceOutputNode(ForceNodeBase, ObjectNode):
'''Force Output'''
bl_idname = 'ForceOutputNode'
bl_label = 'Output'
bl_icon = 'FORCE_FORCE'
def init(self, context):
self.inputs.new('NodeSocketVector', "Force")
self.inputs.new('NodeSocketVector', "Impulse")
def compile(self, compiler):
compiler.map_input(0, compiler.graph_output("force"))
compiler.map_input(1, compiler.graph_output("impulse"))
class PointDataNode(ForceNodeBase, ObjectNode):
'''Input data of physical points'''
bl_idname = 'ForcePointDataNode'
bl_label = 'Point Data'
def init(self, context):
self.outputs.new('NodeSocketVector', "Position")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
compiler.map_output(0, compiler.graph_input("effector.position"))
compiler.map_output(1, compiler.graph_input("effector.velocity"))
class ForceClosestPointNode(ForceNodeBase, ObjectNode):
'''Closest point on the effector mesh'''
bl_idname = 'ForceClosestPointNode'
bl_label = 'Closest Point'
bl_icon = 'FORCE_FORCE'
def init(self, context):
self.inputs.new('NodeSocketVector', "Vector")
self.outputs.new('NodeSocketVector', "Position")
self.outputs.new('NodeSocketVector', "Normal")
self.outputs.new('NodeSocketVector', "Tangent")
def compile(self, compiler):
node = compiler.add_node("EFFECTOR_CLOSEST_POINT", self.name+"N")
compiler.link(compiler.graph_input("effector.object"), node.inputs["object"])
compiler.map_input(0, node.inputs["vector"])
compiler.map_output(0, node.outputs["position"])
compiler.map_output(1, node.outputs["normal"])
compiler.map_output(2, node.outputs["tangent"])
###############################################################################
class ForceFieldNodesNew(Operator):
"""Create new force field node tree"""
bl_idname = "object_nodes.forcefield_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
default="ForceFieldNodes",
)
@classmethod
def make_node_tree(cls, name="ForceFieldNodes"):
ntree = bpy.data.node_groups.new(name, ForceFieldNodeTree.bl_idname)
if ntree:
ntree.init_default()
return ntree
def execute(self, context):
node = getattr(context, "node", None)
ntree = self.make_node_tree(self.name)
if ntree is None:
return {'CANCELLED'}
if node:
node.id = ntree
return {'FINISHED'}
###############################################################################
def register():
bpy.utils.register_module(__name__)
node_categories = [
ForceFieldNodeCategory("FORCE_INPUT", "Input", items=[
NodeItem("ForcePointDataNode"),
]),
ForceFieldNodeCategory("FORCE_OUTPUT", "Output", items=[
NodeItem("ForceOutputNode"),
]),
ForceFieldNodeCategory("FORCE_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
]),
ForceFieldNodeCategory("FORCE_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
]),
ForceFieldNodeCategory("FORCE_GEOMETRY", "Geometry", items=[
NodeItem("ForceClosestPointNode"),
]),
]
nodeitems_utils.register_node_categories("FORCEFIELD_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("FORCEFIELD_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,497 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, ObjectNode, NodeTree, Node
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase, DynamicSocketListNode, enum_property_copy, enum_property_value_prop
import group_nodes
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class GeometryNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'GeometryNodeTree'
###############################################################################
class GeometryNodeTree(NodeTreeBase, NodeTree):
'''Geometry nodes'''
bl_idname = 'GeometryNodeTree'
bl_label = 'Geometry Nodes'
bl_icon = 'MESH_DATA'
# does not show up in the editor header
@classmethod
def poll(cls, context):
return False
def init_default(self):
mesh = self.nodes.new(GeometryMeshLoadNode.bl_idname)
mesh.location = (-100 - mesh.bl_width_default, 20)
out = self.nodes.new(GeometryOutputNode.bl_idname)
out.location = (100, 20)
self.links.new(mesh.outputs[0], out.inputs[0])
class GeometryNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'GeometryNodeTree'
def compile_object_transform(compiler, ob):
node = compiler.add_node("OBJECT_TRANSFORM")
compiler.link(ob, node.inputs[0])
return node.outputs[0]
def compile_matrix_inverse(compiler, mat):
node = compiler.add_node("INVERT_MATRIX44")
compiler.link(mat, node.inputs[0])
return node.outputs[0]
def compile_matrix_multiply(compiler, mat1, mat2):
node = compiler.add_node("MUL_MATRIX44")
compiler.link(mat1, node.inputs[0])
compiler.link(mat2, node.inputs[1])
return node.outputs[0]
def compile_object(compiler, ptr):
key = compiler.get_id_key(ptr)
node = compiler.add_node("OBJECT_LOOKUP")
node.inputs[0].set_value(key)
return node.outputs[0]
# typical inputs of a modifier node:
# (object, transform, inverse transform)
def compile_modifier_inputs(compiler, obptr):
ptfm = compile_object_transform(compiler, compiler.graph_input("modifier.object"))
pitfm = compile_matrix_inverse(compiler, ptfm)
ob = compile_object(compiler, obptr)
obtfm = compile_object_transform(compiler, ob)
tfm = compile_matrix_multiply(compiler, pitfm, obtfm)
itfm = compile_matrix_inverse(compiler, tfm)
return ob, tfm, itfm
class GeometryOutputNode(GeometryNodeBase, ObjectNode, DynamicSocketListNode):
'''Geometry output'''
bl_idname = 'GeometryOutputNode'
bl_label = 'Output'
def init(self, context):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket')
def update(self):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket')
def insert_link(self, link):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket', insert=link.to_socket)
def compile(self, compiler):
result = self.compile_socket_list(compiler, self.inputs, "PASS_MESH", "MESH_COMBINE", "VALUE_MESH")
compiler.link(result, compiler.graph_output("mesh"))
class GeometryElementInfoNode(GeometryNodeBase, ObjectNode):
'''Properties of the current geometry element'''
bl_idname = 'GeometryElementInfoNode'
bl_label = 'Element Info'
def init(self, context):
self.outputs.new('NodeSocketInt', "Index")
self.outputs.new('NodeSocketVector', "Location")
def compile(self, compiler):
compiler.map_output(0, compiler.graph_input("element.index"))
compiler.map_output(1, compiler.graph_input("element.location"))
class GeometryMeshLoadNode(GeometryNodeBase, ObjectNode):
'''Mesh object data'''
bl_idname = 'GeometryMeshLoadNode'
bl_label = 'Mesh'
def init(self, context):
self.outputs.new('GeometrySocket', "")
def compile(self, compiler):
node = compiler.add_node("MESH_LOAD", self.name)
compiler.link(compiler.graph_input("modifier.base_mesh"), node.inputs[0])
compiler.map_output(0, node.outputs[0])
class GeometryMeshLoadObjectNode(GeometryNodeBase, ObjectNode):
'''Load the final mesh of an object'''
bl_idname = 'GeometryMeshLoadObjectNode'
bl_label = 'Object Mesh'
bl_id_property_type = 'OBJECT'
def bl_id_property_poll(self, ob):
return ob.type == 'MESH'
def draw_buttons(self, context, layout):
layout.template_ID(self, "id")
def eval_dependencies(self, depsnode):
ob = self.id
if ob:
depsnode.add_object_relation(ob, 'GEOMETRY')
def init(self, context):
self.outputs.new('GeometrySocket', "")
def compile(self, compiler):
if self.id is None:
return
ob, tfm, itfm = compile_modifier_inputs(compiler, self.id)
node = compiler.add_node("MESH_LOAD_OBJECT")
compiler.link(ob, node.inputs[0])
compiler.map_output(0, node.outputs[0])
class GeometryMeshCombineNode(GeometryNodeBase, ObjectNode, DynamicSocketListNode):
'''Combine multiple meshes into one'''
bl_idname = 'GeometryMeshCombineNode'
bl_label = 'Combine Meshes'
def init(self, context):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket')
self.outputs.new('GeometrySocket', "")
def update(self):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket')
def insert_link(self, link):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'GeometrySocket', insert=link.to_socket)
def compile(self, compiler):
result = self.compile_socket_list(compiler, self.inputs, "PASS_MESH", "MESH_COMBINE", "VALUE_MESH")
compiler.map_output(0, result)
class GeometryMeshArrayNode(GeometryNodeBase, ObjectNode):
'''Make a number of transformed copies of a mesh'''
bl_idname = 'GeometryMeshArrayNode'
bl_label = 'Array'
def init(self, context):
self.inputs.new('GeometrySocket', "")
self.inputs.new('NodeSocketInt', "Count")
self.inputs.new('TransformSocket', "Transform")
self.outputs.new('GeometrySocket', "")
def compile(self, compiler):
node = compiler.add_node("MESH_ARRAY")
compiler.map_input(0, node.inputs["mesh_in"])
compiler.map_input(1, node.inputs["count"])
compiler.map_input(2, node.inputs["transform"])
compiler.map_output(0, node.outputs["mesh_out"])
class GeometryMeshDisplaceNode(GeometryNodeBase, ObjectNode):
'''Add an offset vector to each vertex location'''
bl_idname = 'GeometryMeshDisplaceNode'
bl_label = 'Displace'
def init(self, context):
self.inputs.new('GeometrySocket', "")
self.inputs.new('NodeSocketVector', "Vector")
self.outputs.new('GeometrySocket', "")
def compile(self, compiler):
node = compiler.add_node("MESH_DISPLACE")
compiler.map_input(0, node.inputs["mesh_in"])
compiler.map_input(1, node.inputs["vector"])
compiler.map_output(0, node.outputs["mesh_out"])
class GeometryBooleanNode(GeometryNodeBase, ObjectNode):
'''Boolean operation with another mesh'''
bl_idname = 'GeometryBooleanNode'
bl_label = 'Boolean'
bl_id_property_type = 'OBJECT'
def bl_id_property_poll(self, ob):
return ob.type == 'MESH'
operation = enum_property_copy(bpy.types.BooleanModifier, "operation")
operation_value = enum_property_value_prop("operation")
use_separate = BoolProperty(name="Separate",
description="Keep edges separate",
default=False)
use_dissolve = BoolProperty(name="Dissolve",
description="Dissolve verts created from tessellated intersection",
default=True)
use_connect_regions = BoolProperty(name="Calculate Holes",
description="Connect regions (needed for hole filling)",
default=True)
threshold = FloatProperty(name="Threshold",
default=0.0,
min=0.0,
max=1.0)
def draw_buttons(self, context, layout):
layout.template_ID(self, "id")
layout.prop(self, "operation")
layout.prop(self, "use_separate")
layout.prop(self, "use_dissolve")
layout.prop(self, "use_connect_regions")
layout.prop(self, "threshold")
def eval_dependencies(self, depsnode):
curveob = self.id
if curveob:
depsnode.add_object_relation(curveob, 'TRANSFORM')
depsnode.add_object_relation(curveob, 'GEOMETRY')
def init(self, context):
self.inputs.new('GeometrySocket', "")
self.outputs.new('GeometrySocket', "")
def compile(self, compiler):
if self.id is None:
return
ob, tfm, itfm = compile_modifier_inputs(compiler, self.id)
node = compiler.add_node("MESH_BOOLEAN")
compiler.map_input(0, node.inputs[0])
compiler.link(ob, node.inputs[1])
compiler.link(tfm, node.inputs[2])
compiler.link(itfm, node.inputs[3])
node.inputs[4].set_value(self.operation_value)
node.inputs[5].set_value(self.use_separate)
node.inputs[6].set_value(self.use_dissolve)
node.inputs[7].set_value(self.use_connect_regions)
node.inputs[8].set_value(self.threshold)
compiler.map_output(0, node.outputs[0])
class GeometryClosestPointNode(GeometryNodeBase, ObjectNode):
'''Closest point on the a mesh'''
bl_idname = 'GeometryClosestPointNode'
bl_label = 'Closest Point'
def init(self, context):
self.inputs.new('GeometrySocket', "Mesh")
self.inputs.new('NodeSocketVector', "Vector")
self.outputs.new('NodeSocketVector', "Position")
self.outputs.new('NodeSocketVector', "Normal")
self.outputs.new('NodeSocketVector', "Tangent")
def compile(self, compiler):
node = compiler.add_node("MESH_CLOSEST_POINT")
compiler.map_input(0, node.inputs["mesh"])
compiler.map_input(1, node.inputs["vector"])
compiler.map_output(0, node.outputs["position"])
compiler.map_output(1, node.outputs["normal"])
compiler.map_output(2, node.outputs["tangent"])
###############################################################################
class CurveNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return isinstance(ntree, NodeTreeBase)
class CurvePathNode(CurveNodeBase, ObjectNode):
'''Get curve geometry values'''
bl_idname = 'CurvePathNode'
bl_label = 'Curve Path'
bl_id_property_type = 'OBJECT'
def bl_id_property_poll(self, ob):
return ob.type == 'CURVE'
def draw_buttons(self, context, layout):
layout.template_ID(self, "id")
def eval_dependencies(self, depsnode):
curveob = self.id
if curveob:
depsnode.add_object_relation(curveob, 'TRANSFORM')
depsnode.add_object_relation(curveob, 'GEOMETRY')
def init(self, context):
self.inputs.new('NodeSocketFloat', "Parameter")
self.outputs.new('NodeSocketVector', "Location")
self.outputs.new('NodeSocketVector', "Direction")
self.outputs.new('NodeSocketVector', "Normal")
self.outputs.new('TransformSocket', "Rotation")
self.outputs.new('NodeSocketFloat', "Radius")
self.outputs.new('NodeSocketFloat', "Weight")
self.outputs.new('NodeSocketFloat', "Tilt")
def compile(self, compiler):
if self.id is None:
return
ob, tfm, itfm = compile_modifier_inputs(compiler, self.id)
node = compiler.add_node("CURVE_PATH")
compiler.link(ob, node.inputs[0])
compiler.link(tfm, node.inputs[1])
compiler.link(itfm, node.inputs[2])
compiler.map_input(0, node.inputs[3])
compiler.map_output(0, node.outputs[0])
compiler.map_output(1, node.outputs[1])
compiler.map_output(2, node.outputs[2])
compiler.map_output(3, node.outputs[3])
compiler.map_output(4, node.outputs[4])
compiler.map_output(5, node.outputs[5])
compiler.map_output(6, node.outputs[6])
###############################################################################
class GeometryNodesNew(Operator):
"""Create new geometry node tree"""
bl_idname = "object_nodes.geometry_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
default="GeometryNodes",
)
@classmethod
def make_node_tree(cls, name="GeometryNodes"):
ntree = bpy.data.node_groups.new(name, GeometryNodeTree.bl_idname)
if ntree:
ntree.init_default()
return ntree
def execute(self, context):
node = getattr(context, "node", None)
ntree = self.make_node_tree(self.name)
if ntree is None:
return {'CANCELLED'}
if node:
node.id = ntree
return {'FINISHED'}
###############################################################################
def register():
bpy.utils.register_module(__name__)
gnode, ginput, goutput = group_nodes.make_node_group_types(
"Geometry", GeometryNodeTree, GeometryNodeBase)
node_categories = [
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("ObjectRangeNode"),
NodeItem("GeometryMeshLoadNode"),
NodeItem("GeometryMeshLoadObjectNode"),
NodeItem(ginput.bl_idname),
NodeItem("GeometryElementInfoNode"),
NodeItem("ObjectValueFloatNode"),
NodeItem("ObjectValueIntNode"),
NodeItem("ObjectValueVectorNode"),
NodeItem("ObjectValueColorNode"),
]),
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
NodeItem("GeometryOutputNode"),
NodeItem(goutput.bl_idname),
]),
GeometryNodeCategory("GEO_MODIFIER", "Modifier", items=[
NodeItem("GeometryMeshArrayNode"),
NodeItem("GeometryMeshDisplaceNode"),
NodeItem("GeometryBooleanNode"),
]),
GeometryNodeCategory("GEO_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
NodeItem("ObjectSeparateMatrixNode"),
NodeItem("ObjectCombineMatrixNode"),
NodeItem("GeometryMeshCombineNode"),
]),
GeometryNodeCategory("GEO_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
NodeItem("ObjectMatrixMathNode"),
NodeItem("ObjectTransformVectorNode"),
NodeItem("ObjectTranslationTransformNode"),
NodeItem("ObjectEulerTransformNode"),
NodeItem("ObjectAxisAngleTransformNode"),
NodeItem("ObjectScaleTransformNode"),
NodeItem("ObjectGetTranslationNode"),
NodeItem("ObjectGetEulerNode"),
NodeItem("ObjectGetAxisAngleNode"),
NodeItem("ObjectGetScaleNode"),
NodeItem("ObjectRandomNode"),
NodeItem("GeometryClosestPointNode"),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
NodeItem("ImageSampleNode"),
NodeItem("ObjectTextureCloudsNode"),
NodeItem("ObjectTextureDistNoiseNode"),
NodeItem("ObjectTextureGaborNoiseNode"),
NodeItem("ObjectTextureMagicNode"),
NodeItem("ObjectTextureMarbleNode"),
NodeItem("ObjectTextureMusgraveNode"),
NodeItem("ObjectTextureStucciNode"),
NodeItem("ObjectTextureVoronoiNode"),
NodeItem("ObjectTextureWoodNode"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
NodeItem("CurvePathNode"),
]),
group_nodes.GroupNodeCategory("GEO", gnode, ginput, goutput),
]
nodeitems_utils.register_node_categories("GEOMETRY_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("GEOMETRY_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,674 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, Panel, UIList, NodeTree, Node, NodeSocket, ObjectNode, PropertyGroup, BVMTypeDesc
from bpy.props import *
from socket_types import socket_type_items, socket_type_to_rna, socket_type_to_bvm_type, rna_to_socket_type
from nodeitems_utils import NodeCategory as NodeCategoryBase, NodeItem, NodeItemCustom
###############################################################################
# Group Interface
def make_node_group_interface(prefix, treetype, tree_items_update):
_in_out_items = [('IN', "In", "Input"), ('OUT', "Out", "Output")]
prop_name = StringProperty(name="Name", default="Value", update=tree_items_update)
prop_base_type = EnumProperty(name="Base Type", items=socket_type_items, default='FLOAT', update=tree_items_update)
prop_in_out = EnumProperty(name="In/Out", items=_in_out_items, default='IN')
# XXX PropertyGroup does not have a bl_idname,
# so we can only construct a dynamic type name
# through the python 'type()' function
d = { "name" : prop_name,
"base_type" : prop_base_type,
}
item_type = type("%sNodeGroupItem" % prefix, (PropertyGroup,), d)
bpy.utils.register_class(item_type)
treetype.inputs = CollectionProperty(type=item_type)
treetype.outputs = CollectionProperty(type=item_type)
def add_input(self, name, base_type):
item = self.inputs.add()
item.name = name
item.base_type = base_type
tree_items_update(self)
def add_output(self, name, base_type):
item = self.outputs.add()
item.name = name
item.base_type = base_type
tree_items_update(self)
def remove_input(self, index):
self.inputs.remove(index)
tree_items_update(self)
def remove_output(self, index):
self.outputs.remove(index)
tree_items_update(self)
treetype.add_input = add_input
treetype.add_output = add_output
treetype.remove_input = remove_input
treetype.remove_output = remove_output
# -------------------------------------------------------------------------
class OperatorBase():
@classmethod
def poll(cls, context):
space = context.space_data
if space.type != 'NODE_EDITOR':
return False
ntree = space.edit_tree
if not (ntree and isinstance(ntree, treetype)):
return False
return True
class NodeGroupItemAdd(OperatorBase, Operator):
"""Add a node group interface socket"""
bl_idname = "object_nodes.%s_nodegroup_item_add" % prefix.lower()
bl_label = "Add Socket"
bl_options = {'REGISTER', 'UNDO'}
name = prop_name
base_type = prop_base_type
in_out = prop_in_out
def execute(self, context):
ntree = context.space_data.edit_tree
if self.in_out == 'IN':
ntree.add_input(self.name, self.base_type)
if self.in_out == 'OUT':
ntree.add_output(self.name, self.base_type)
return {'FINISHED'}
class NodeGroupItemRemove(OperatorBase, Operator):
"""Remove a node group interface socket"""
bl_idname = "object_nodes.%s_nodegroup_item_remove" % prefix.lower()
bl_label = "Remove Socket"
bl_options = {'REGISTER', 'UNDO'}
index = IntProperty(name="Index")
in_out = prop_in_out
def execute(self, context):
ntree = context.space_data.edit_tree
if self.in_out == 'IN':
ntree.remove_input(self.index)
if self.in_out == 'OUT':
ntree.remove_output(self.index)
return {'FINISHED'}
bpy.utils.register_class(NodeGroupItemAdd)
bpy.utils.register_class(NodeGroupItemRemove)
# -------------------------------------------------------------------------
class NodeGroupInputList(UIList):
bl_idname = "OBJECT_NODES_UL_%s_nodegroup_inputs" % prefix.lower()
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
layout.prop(item, "name", text="", emboss=False)
layout.prop(item, "base_type", text="")
props = layout.operator(NodeGroupItemRemove.bl_idname, text="", icon='X')
props.index = index
props.in_out = 'IN'
class NodeGroupOutputList(UIList):
bl_idname = "OBJECT_NODES_UL_%s_nodegroup_outputs" % prefix.lower()
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
layout.prop(item, "name", text="", emboss=False)
layout.prop(item, "base_type", text="")
props = layout.operator(NodeGroupItemRemove.bl_idname, text="", icon='X')
props.index = index
props.in_out = 'OUT'
bpy.utils.register_class(NodeGroupInputList)
bpy.utils.register_class(NodeGroupOutputList)
class NodeGroupInterfacePanel(Panel):
"""Interface setup of a node group tree"""
bl_label = "Interface"
bl_idname = "OBJECT_NODES_PT_%s_nodegroup_interface" % prefix.lower()
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
@classmethod
def poll(cls, context):
space = context.space_data
if space.type != 'NODE_EDITOR':
return False
ntree = space.edit_tree
if not (ntree and isinstance(ntree, treetype)):
return False
return True
def draw(self, context):
ntree = context.space_data.edit_tree
layout = self.layout
row = layout.row()
col = row.column(align=True)
col.template_list(NodeGroupInputList.bl_idname, "inputs", ntree, "inputs", ntree, "active_input")
props = col.operator(NodeGroupItemAdd.bl_idname, text="", icon='ZOOMIN')
props.in_out = 'IN'
col = row.column(align=True)
col.template_list(NodeGroupOutputList.bl_idname, "outputs", ntree, "outputs", ntree, "active_output")
props = col.operator(NodeGroupItemAdd.bl_idname, text="", icon='ZOOMIN')
props.in_out = 'OUT'
bpy.utils.register_class(NodeGroupInterfacePanel)
###############################################################################
# common identifying class to find group nodes
class GroupNodeBase():
pass
def internal_group_trees(ntree, visited=None):
if ntree is None:
return
if visited is None:
visited = set()
elif ntree in visited:
return
visited.add(ntree)
yield ntree
for node in ntree.nodes:
if not isinstance(node, GroupNodeBase):
continue
for itree in internal_group_trees(node.id, visited):
yield itree
def ancestor_trees(root_tree, all_trees):
ancestors = set()
if root_tree is None:
return ancestors
visited = set()
def visit(ntree):
global indent
if ntree is None:
return False
if ntree in visited:
return ntree in ancestors
visited.add(ntree)
for node in ntree.nodes:
if not isinstance(node, GroupNodeBase):
continue
elif node.id == root_tree or visit(node.id):
ancestors.add(ntree)
return True
return False
for ntree in all_trees:
visit(ntree)
return ancestors
def make_node_group_types(prefix, treetype, node_base):
ntree_idname = treetype.bl_rna.identifier
groupnode_idname = '%sGroupNode' % prefix
class NodeGroupNew(Operator):
"""Create new object node tree"""
bl_idname = "object_nodes.%s_nodegroup_new" % prefix.lower()
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
bl_ntree_idname = ntree_idname
name = StringProperty(
name="Name",
default="%s Node Group" % prefix,
)
@classmethod
def init_default(cls, ntree):
gin = ntree.nodes.new(GroupInputNode.bl_idname)
gin.location = (-100 - gin.bl_width_default, 20)
gout = ntree.nodes.new(GroupOutputNode.bl_idname)
gout.location = (100, 20)
def execute(self, context):
node = getattr(context, "node", None)
ntree = bpy.data.node_groups.new(self.name, self.bl_ntree_idname)
if ntree is None:
return {'CANCELLED'}
self.init_default(ntree)
if node:
node.id = ntree
return {'FINISHED'}
# updates all affected nodes when the interface changes
def tree_items_update(self, context=bpy.context):
gtree = self.id_data
for node in gtree.nodes:
if isinstance(node, GroupInputNode) or isinstance(node, GroupOutputNode):
node.update()
for ntree in bpy.data.node_groups:
if not isinstance(ntree, treetype):
continue
for node in ntree.nodes:
if isinstance(node, GroupNode):
if node.id == gtree:
node.update()
def node_sockets_sync(sockets, items):
free_sockets = set(s for s in sockets)
free_items = set(i for i in items)
def find_match(s):
for i in free_items:
if i.name == s.name and isinstance(s, socket_type_to_rna(i.base_type)):
return i
def socket_index(s):
for k, ts in enumerate(sockets):
if ts == s:
return k
# match up current sockets with items
match = dict()
for s in sockets:
i = find_match(s)
if i is not None:
free_items.remove(i)
free_sockets.remove(s)
match[i] = s
# nothing to do if perfect match
if not (free_items or free_sockets):
return
# remove unmatched sockets
for s in free_sockets:
sockets.remove(s)
# fix socket list
for k, i in enumerate(items):
s = match.get(i, None)
if s is None:
# add socket for unmatched item
stype = socket_type_to_rna(i.base_type)
s = sockets.new(stype.bl_rna.identifier, i.name)
index = socket_index(s)
if index != k:
sockets.move(index, k)
class GroupNode(node_base, ObjectNode, GroupNodeBase):
'''Group of nodes that can be used in other trees'''
bl_idname = groupnode_idname
bl_label = 'Group'
bl_ntree_idname = ntree_idname
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
if not isinstance(ntree, treetype):
return False
parent_tree = self.id_data
for itree in internal_group_trees(ntree):
if itree == parent_tree:
return False
return True
def bl_id_property_update(self, context):
self.update()
def poll_instance(self, ntree):
if hasattr(super(), "poll_instance") and not super().poll_instance(ntree):
return False
if self.id == ntree:
return False
for itree in internal_group_trees(self.id):
if itree == ntree:
return False
return True
def draw_buttons(self, context, layout):
layout.template_ID(self, "id", new=NodeGroupNew.bl_idname)
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
# changes to the group tree require own recompile
depsnode.add_nodetree_relation(ntree, 'PARAMETERS')
# add internal dependencies of the group
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def update(self):
if self.is_updating:
return
with self.update_lock():
gtree = self.id
node_sockets_sync(self.inputs, gtree.inputs if gtree else [])
node_sockets_sync(self.outputs, gtree.outputs if gtree else [])
def compile(self, compiler):
if self.id is not None:
self.id.compile_nodes(compiler)
class GroupInputNode(node_base, ObjectNode):
'''Inputs of the node group inside the tree'''
bl_idname = '%sGroupInputNode' % prefix
bl_label = 'Group Inputs'
def update(self):
if self.is_updating:
return
with self.update_lock():
node_sockets_sync(self.outputs, self.id_data.inputs)
def compile(self, compiler):
gtree = self.id_data
for i, item in enumerate(gtree.inputs):
proxy = compiler.add_proxy(socket_type_to_bvm_type(item.base_type))
compiler.map_output(i, proxy.outputs[0])
compiler.map_input_external(i, proxy.inputs[0])
class GroupOutputNode(node_base, ObjectNode):
'''Outputs of the node group inside the tree'''
bl_idname = '%sGroupOutputNode' % prefix
bl_label = 'Group Outputs'
def update(self):
if self.is_updating:
return
with self.update_lock():
node_sockets_sync(self.inputs, self.id_data.outputs)
def compile(self, compiler):
gtree = self.id_data
for i, item in enumerate(gtree.outputs):
proxy = compiler.add_proxy(socket_type_to_bvm_type(item.base_type))
compiler.map_input(i, proxy.inputs[0])
compiler.map_output_external(i, proxy.outputs[0])
make_node_group_interface(prefix, treetype, tree_items_update)
bpy.utils.register_class(NodeGroupNew)
bpy.utils.register_class(GroupNode)
bpy.utils.register_class(GroupInputNode)
bpy.utils.register_class(GroupOutputNode)
return GroupNode, GroupInputNode, GroupOutputNode
###############################################################################
def GroupNodeCategory(prefix, gnode, ginput, goutput):
ntree_idname = gnode.bl_ntree_idname
def copy_node_attributes(dst, src):
copy_attrs = ['color', 'height', 'hide', 'label',
'location', 'id', 'mute', 'name',
'show_options', 'show_preview', 'show_texture', 'use_custom_color',
'width', 'width_hidden']
for attr in copy_attrs:
if not hasattr(dst, attr):
continue
if dst.is_property_readonly(attr):
continue
setattr(dst, attr, getattr(src, attr))
for key, value in src.items():
dst[key] = value
def copy_nodes(nodes, to_ntree):
new_nodes = dict()
new_sockets = dict()
# copy nodes and local attributes
for node in nodes:
inode = to_ntree.nodes.new(node.bl_idname)
copy_node_attributes(inode, node)
# map old to new
new_nodes[node] = inode
for old, new in zip(node.inputs, inode.inputs):
new_sockets[old] = new
for old, new in zip(node.outputs, inode.outputs):
new_sockets[old] = new
# parent node must be mapped to new nodes
for node, inode in new_nodes.items():
if node.parent:
iparent = new_nodes.get(node.parent, None)
if iparent:
inode.parent = iparent
return new_nodes, new_sockets
def node_bounds(nodes):
if nodes:
bbmin = (min(node.location[0] for node in nodes),
min(node.location[1] - node.height for node in nodes))
bbmax = (max(node.location[0] + node.width for node in nodes),
max(node.location[1] for node in nodes))
else:
bbmin = (0.0, 0.0)
bbmax = (0.0, 0.0)
return bbmin, bbmax
class NodeGroupMake(Operator):
"""Make a node group from selected nodes"""
bl_idname = "object_nodes.%s_nodegroup_make" % prefix.lower()
bl_label = "Make Group"
bl_options = {'REGISTER', 'UNDO'}
bl_ntree_idname = ntree_idname
name = StringProperty(
name="Name",
default="Node Group",
)
@classmethod
def poll(cls, context):
space = context.space_data
if not space or space.type != 'NODE_EDITOR':
return False
ntree = space.edit_tree
if not ntree or not gnode.poll(ntree):
return False
return True
def cache_selection(self, ntree):
selected_nodes = [node for node in ntree.nodes if node.select]
internal_links = []
input_links = []
output_links = []
for link in ntree.links:
if link.is_hidden or not link.is_valid:
continue
sel_from = link.from_node.select
sel_to = link.to_node.select
if not sel_from and not sel_to:
continue
elif sel_from and sel_to:
internal_links.append(link)
elif sel_from:
output_links.append(link)
elif sel_to:
input_links.append(link)
return selected_nodes, internal_links, input_links, output_links
def layout_group(self, group_node, new_nodes, input_node, output_node):
bbmin, bbmax = node_bounds(new_nodes)
center = (0.5*(bbmin[0] + bbmax[0]), 0.5*(bbmin[1] + bbmax[1]))
for node in new_nodes.values():
node.location[0] -= center[0]
node.location[1] -= center[1]
offsetx = 0.5*(bbmax[0] - bbmin[0])
input_node.location[0] = -offsetx - input_node.bl_width_default - 50
input_node.location[1] = 0.5*input_node.bl_height_default
output_node.location[0] = offsetx + 50
output_node.location[1] = 0.5*output_node.bl_height_default
group_node.location[0] = center[0] - 0.5*group_node.bl_width_default
group_node.location[1] = center[1] + 0.5*group_node.bl_height_default
def execute(self, context):
ntree = context.space_data.edit_tree
# Warning! this has to happen right at the start
# because creating a node will make it selected by default!
selected_nodes, internal_links, input_links, output_links = self.cache_selection(ntree)
groupnode = ntree.nodes.new(gnode.bl_idname)
if groupnode is None:
return {'CANCELLED'}
grouptree = bpy.data.node_groups.new(self.name, self.bl_ntree_idname)
if grouptree is None:
return {'CANCELLED'}
input_node = grouptree.nodes.new(ginput.bl_idname)
output_node = grouptree.nodes.new(goutput.bl_idname)
groupnode.id = grouptree
# copy nodes and attributes
new_nodes, new_sockets = copy_nodes(selected_nodes, grouptree)
# move nodes to sensible locations
self.layout_group(groupnode, new_nodes, input_node, output_node)
# define the group interface
io_inputs = dict()
io_outputs = dict()
for link in input_links:
if link.from_socket not in io_inputs:
io_inputs[link.from_socket] = {link.to_socket,}
else:
io_inputs[link.from_socket].add(link.to_socket)
for link in output_links:
if link.from_socket not in io_outputs:
io_outputs[link.from_socket] = {link.to_socket,}
else:
io_outputs[link.from_socket].add(link.to_socket)
# reconstruct links
for link in internal_links:
ifrom_socket = new_sockets[link.from_socket]
ito_socket = new_sockets[link.to_socket]
grouptree.links.new(ifrom_socket, ito_socket)
for io, targets in io_inputs.items():
grouptree.add_input(io.name, rna_to_socket_type(type(io)))
# XXX this shouldn't be necessary, but node updates are terrible
groupnode.update()
io_extern = groupnode.inputs[-1]
io_intern = input_node.outputs[-1]
ntree.links.new(io, io_extern)
for s in targets:
grouptree.links.new(io_intern, new_sockets[s])
for io, targets in io_outputs.items():
grouptree.add_output(io.name, rna_to_socket_type(type(io)))
# XXX this shouldn't be necessary, but node updates are terrible
groupnode.update()
io_extern = groupnode.outputs[-1]
io_intern = output_node.inputs[-1]
grouptree.links.new(new_sockets[io], io_intern)
for s in targets:
ntree.links.new(io_extern, s)
# delete replaced nodes
for node in selected_nodes:
ntree.nodes.remove(node)
# clean up selection
for node in grouptree.nodes:
node.select = False
grouptree.nodes.active = None
for node in ntree.nodes:
node.select = (node == groupnode)
ntree.nodes.active = groupnode
return {'FINISHED'}
# menu entry for node group tools
def group_tools_draw(self, layout, context):
# TODO C node operators won't work for our nodes
#layout.operator("node.group_make")
#layout.operator("node.group_ungroup")
layout.separator()
def node_group_items(context):
if context is None:
return
space = context.space_data
if not space:
return
ntree = space.edit_tree
if not ntree:
return
yield NodeItemCustom(draw=group_tools_draw)
all_trees = context.blend_data.node_groups
ancestors = ancestor_trees(ntree, all_trees)
free_trees = [t for t in all_trees if (t.bl_rna.identifier == gnode.bl_ntree_idname) and (t != ntree) and (t not in ancestors)]
for ntree in free_trees:
yield NodeItem(gnode.bl_idname,
label=ntree.name,
settings={"id": "bpy.data.node_groups[%r]" % ntree.name})
class NodeCategory(NodeCategoryBase):
def __init__(self):
super().__init__("%s_GROUPS" % prefix, "Group", items=node_group_items)
@classmethod
def poll(cls, context):
ntree = context.space_data.edit_tree
if not ntree:
return False
return gnode.poll(ntree)
bpy.utils.register_class(NodeGroupMake)
# create keymap
wm = bpy.context.window_manager
km = wm.keyconfigs.default.keymaps.new(name="Node Generic", space_type='NODE_EDITOR')
kmi = km.keymap_items.new(NodeGroupMake.bl_idname, 'G', 'PRESS', ctrl=True)
keymaps.append(km)
return NodeCategory()
###############################################################################
keymaps = []
def register():
pass
def unregister():
# remove keymap
wm = bpy.context.window_manager
for km in keymaps:
wm.keyconfigs.default.keymaps.remove(km)
keymaps.clear()

View File

@@ -0,0 +1,240 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, ObjectNode, NodeTree, Node
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase, DynamicSocketListNode, enum_property_copy, enum_property_value_prop
import group_nodes
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class InstancingNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'InstancingNodeTree'
###############################################################################
class InstancingNodeTree(NodeTreeBase, NodeTree):
'''Instancing nodes'''
bl_idname = 'InstancingNodeTree'
bl_label = 'Instancing Nodes'
bl_icon = 'EMPTY_DATA'
# does not show up in the editor header
@classmethod
def poll(cls, context):
return False
def init_default(self):
out = self.nodes.new(OutputNode.bl_idname)
out.location = (100, 20)
class InstancingNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'InstancingNodeTree'
def compile_object_transform(compiler, ob):
node = compiler.add_node("OBJECT_TRANSFORM")
compiler.link(ob, node.inputs[0])
return node.outputs[0]
def compile_matrix_inverse(compiler, mat):
node = compiler.add_node("INVERT_MATRIX44")
compiler.link(mat, node.inputs[0])
return node.outputs[0]
def compile_matrix_multiply(compiler, mat1, mat2):
node = compiler.add_node("MUL_MATRIX44")
compiler.link(mat1, node.inputs[0])
compiler.link(mat2, node.inputs[1])
return node.outputs[0]
def compile_object(compiler, ptr):
key = compiler.get_id_key(ptr)
node = compiler.add_node("OBJECT_LOOKUP")
node.inputs[0].set_value(key)
return node.outputs[0]
class OutputNode(InstancingNodeBase, ObjectNode, DynamicSocketListNode):
'''Dupli output'''
bl_idname = 'InstancingOutputNode'
bl_label = 'Output'
def init(self, context):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'DupliSocket')
def update(self):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'DupliSocket')
def insert_link(self, link):
if self.is_updating:
return
with self.update_lock():
self.update_socket_list(self.inputs, 'DupliSocket', insert=link.to_socket)
def compile(self, compiler):
result = self.compile_socket_list(compiler, self.inputs, "PASS_DUPLIS", "DUPLIS_COMBINE", "VALUE_DUPLIS")
compiler.link(result, compiler.graph_output("dupli.result"))
class MakeDupliNode(InstancingNodeBase, ObjectNode):
'''Make object instance'''
bl_idname = 'InstancingMakeDupliNode'
bl_label = 'Make Dupli'
bl_id_property_type = 'OBJECT'
def draw_buttons(self, context, layout):
layout.template_ID(self, "id")
def eval_dependencies(self, depsnode):
dob = self.id
if dob:
# XXX not quite ideal: we need to define a "dependency"
# because this puts the dupli object into the globals dict,
# even though the duplis don't actually need to be re-evaluated
# when this object changes
depsnode.add_object_relation(dob, 'GEOMETRY')
def init(self, context):
self.inputs.new('TransformSocket', "Transform")
self.inputs.new('NodeSocketInt', "Index")
self.inputs.new('NodeSocketInt', "Hide")
socket = self.inputs.new('NodeSocketInt', "Recursive")
socket.default_value = 1
self.outputs.new('DupliSocket', "")
def compile(self, compiler):
ob = compile_object(compiler, self.id)
node = compiler.add_node("MAKE_DUPLI")
compiler.link(ob, node.inputs["object"])
compiler.map_input(0, node.inputs["transform"])
compiler.map_input(1, node.inputs["index"])
compiler.map_input(2, node.inputs["hide"])
compiler.map_input(3, node.inputs["recursive"])
compiler.map_output(0, node.outputs[0])
###############################################################################
class InstancingNodesNew(Operator):
"""Create new Instancing node tree"""
bl_idname = "object_nodes.instancing_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
default="DupliNodes",
)
@classmethod
def make_node_tree(cls, name="DupliNodes"):
ntree = bpy.data.node_groups.new(name, InstancingNodeTree.bl_idname)
if ntree:
ntree.init_default()
return ntree
def execute(self, context):
node = getattr(context, "node", None)
ntree = self.make_node_tree(self.name)
if ntree is None:
return {'CANCELLED'}
if node:
node.id = ntree
return {'FINISHED'}
###############################################################################
def register():
bpy.utils.register_module(__name__)
gnode, ginput, goutput = group_nodes.make_node_group_types(
"Instancing", InstancingNodeTree, InstancingNodeBase)
node_categories = [
InstancingNodeCategory("INS_INPUT", "Input", items=[
NodeItem(ginput.bl_idname),
NodeItem("ObjectValueFloatNode"),
NodeItem("ObjectValueIntNode"),
NodeItem("ObjectValueVectorNode"),
NodeItem("ObjectValueColorNode"),
]),
InstancingNodeCategory("INS_OUTPUT", "Output", items=[
NodeItem("InstancingOutputNode"),
NodeItem(goutput.bl_idname),
]),
InstancingNodeCategory("INS_DUPLIS", "Duplis", items=[
NodeItem("InstancingMakeDupliNode"),
]),
InstancingNodeCategory("INS_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
]),
InstancingNodeCategory("INS_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
NodeItem("ObjectTranslationTransformNode"),
NodeItem("ObjectEulerTransformNode"),
NodeItem("ObjectAxisAngleTransformNode"),
NodeItem("ObjectScaleTransformNode"),
NodeItem("ObjectGetTranslationNode"),
NodeItem("ObjectGetEulerNode"),
NodeItem("ObjectGetAxisAngleNode"),
NodeItem("ObjectGetScaleNode"),
NodeItem("ObjectRandomNode"),
]),
InstancingNodeCategory("INS_TEXTURE", "Texture", items=[
NodeItem("ObjectTextureCloudsNode"),
NodeItem("ObjectTextureDistNoiseNode"),
NodeItem("ObjectTextureGaborNoiseNode"),
NodeItem("ObjectTextureMagicNode"),
NodeItem("ObjectTextureMarbleNode"),
NodeItem("ObjectTextureMusgraveNode"),
NodeItem("ObjectTextureStucciNode"),
NodeItem("ObjectTextureVoronoiNode"),
NodeItem("ObjectTextureWoodNode"),
]),
group_nodes.GroupNodeCategory("INS", gnode, ginput, goutput),
]
nodeitems_utils.register_node_categories("INSTANCING_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("INSTANCING_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,170 @@
# ##### 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>
from bpy.types import BVMEvalGlobals
from collections import OrderedDict
from socket_types import rna_to_bvm_type, convert_sockets
# Utility dict type that works like RNA collections,
# i.e. accepts both string keys and integer indices
class StringDict(OrderedDict):
def __getitem__(self, key):
if isinstance(key, int):
return super().__getitem__(list(super().keys())[key])
else:
return super().__getitem__(key)
def __contains__(self, key):
if isinstance(key, int):
return super().__contains__(list(super().keys())[key])
else:
return super().__contains__(key)
# Wrapper classes to make constructing node graphs as convenient as possible
# RNA does not allow collections of temporary (node,socket) pairs,
# so we use python wrappers to pass them around as a single object
class InputWrapper:
def __init__(self, gnode, ginput):
self.gnode = gnode
self.ginput = ginput
@property
def typedesc(self):
return self.ginput.typedesc
def set_value(self, value):
base_type = self.ginput.typedesc.base_type
if base_type == 'FLOAT':
self.gnode.set_value_float(self.ginput, value)
elif base_type == 'FLOAT3':
self.gnode.set_value_float3(self.ginput, value)
elif base_type == 'FLOAT4':
self.gnode.set_value_float4(self.ginput, value)
elif base_type == 'INT':
self.gnode.set_value_int(self.ginput, value)
elif base_type == 'MATRIX44':
self.gnode.set_value_matrix44(self.ginput, value)
class OutputWrapper:
def __init__(self, gnode, goutput):
self.gnode = gnode
self.goutput = goutput
@property
def typedesc(self):
return self.goutput.typedesc
class NodeWrapper:
def __init__(self, gnode):
self.gnode = gnode
self.inputs = StringDict([ (i.name, InputWrapper(self.gnode, i)) for i in self.gnode.inputs ])
self.outputs = StringDict([ (o.name, OutputWrapper(self.gnode, o)) for o in self.gnode.outputs ])
# Compiler class for converting nodes
class NodeCompiler:
def __init__(self, graph):
self.graph = graph
self.bnode_stack = []
def push(self, bnode, input_map, output_map):
# proxies for inputs/outputs
bnode_inputs = StringDict()
for binput in bnode.inputs:
proxy = self.add_proxy(rna_to_bvm_type(type(binput)))
bnode_inputs[binput.identifier] = proxy
input_map[(bnode, binput)] = proxy.inputs[0]
if hasattr(binput, "default_value"):
proxy.inputs[0].set_value(binput.default_value)
bnode_outputs = StringDict()
for boutput in bnode.outputs:
proxy = self.add_proxy(rna_to_bvm_type(type(boutput)))
bnode_outputs[boutput.identifier] = proxy
output_map[(bnode, boutput)] = proxy.outputs[0]
self.bnode_stack.append((bnode, bnode_inputs, bnode_outputs))
def pop(self):
self.bnode_stack.pop()
def add_node(self, type, name=""):
node = self.graph.add_node(type, name)
if node is None:
raise Exception("Can not add node of type %r" % type)
return NodeWrapper(node)
def add_proxy(self, type):
return self.add_node("PASS_%s" % type)
def graph_input(self, name):
in_node, in_socket = self.graph.get_input(name)
return OutputWrapper(in_node, in_node.outputs[in_socket])
def graph_output(self, name):
out_node, out_socket = self.graph.get_output(name)
return InputWrapper(out_node, out_node.inputs[out_socket])
def link(self, from_output, to_input, autoconvert=True):
if autoconvert:
from_output = convert_sockets(self, from_output, to_input)
if from_output is not None:
to_input.gnode.set_input_link(to_input.ginput, from_output.gnode, from_output.goutput)
def map_input(self, key, socket):
bnode, bnode_inputs, bnode_outputs = self.bnode_stack[-1]
if key not in bnode_inputs:
raise KeyError("Input %r not found in node %r" % (key, bnode))
self.link(bnode_inputs[key].outputs[0], socket)
def map_output(self, key, socket):
bnode, bnode_inputs, bnode_outputs = self.bnode_stack[-1]
if key not in bnode_outputs:
raise KeyError("Output %r not found in node %r" % (key, bnode))
self.link(socket, bnode_outputs[key].inputs[0])
def map_input_external(self, key, socket):
if len(self.bnode_stack) < 2:
return
bnode, bnode_inputs, bnode_outputs = self.bnode_stack[-2]
if key not in bnode_inputs:
raise KeyError("Input %r not found in node %r" % (key, bnode))
self.link(bnode_inputs[key].outputs[0], socket)
def map_output_external(self, key, socket):
if len(self.bnode_stack) < 2:
return
bnode, bnode_inputs, bnode_outputs = self.bnode_stack[-2]
if key not in bnode_outputs:
raise KeyError("Output %r not found in node %r" % (key, bnode))
self.link(socket, bnode_outputs[key].inputs[0])
@staticmethod
def get_id_key(id_data):
return BVMEvalGlobals.get_id_key(id_data) if id_data else 0
###############################################################################
def register():
pass
def unregister():
pass

View File

@@ -0,0 +1,308 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, ObjectNode, NodeTree, Node, NodeSocket
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase
from geometry_nodes import GeometryNodesNew
from instancing_nodes import InstancingNodesNew
from forcefield_nodes import ForceFieldNodesNew
from smokesim_nodes import SmokeSimNodesNew
from rigidbody_nodes import RigidBodyNodesNew
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class ObjectNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'ObjectNodeTree'
###############################################################################
class ObjectNodeTree(NodeTreeBase, NodeTree):
'''Object component nodes'''
bl_idname = 'ObjectNodeTree'
bl_label = 'Object Nodes'
bl_icon = 'OBJECT_DATA'
@classmethod
def get_from_context(cls, context):
ob = context.object
if ob:
return ob.node_tree, ob.node_tree, ob
else:
return None, None, None
class ObjectNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'ObjectNodeTree'
class GeometryNode(ObjectNodeBase, ObjectNode):
'''Geometry'''
bl_idname = 'GeometryNode'
bl_label = 'Geometry'
bl_icon = 'MESH_DATA'
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
return ntree.bl_idname == 'GeometryNodeTree'
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def draw_buttons(self, context, layout):
layout.context_pointer_set("node", self)
layout.template_ID(self, "id", new=GeometryNodesNew.bl_idname)
def init(self, context):
pass
def compile(self, compiler):
pass
class InstancingNode(ObjectNodeBase, ObjectNode):
'''Instancing'''
bl_idname = 'InstancingNode'
bl_label = 'Instancing'
bl_icon = 'EMPTY_DATA'
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
return ntree.bl_idname == 'InstancingNodeTree'
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def draw_buttons(self, context, layout):
layout.context_pointer_set("node", self)
layout.template_ID(self, "id", new=InstancingNodesNew.bl_idname)
def compile(self, compiler):
pass
class ForceFieldNode(ObjectNodeBase, ObjectNode):
'''Force Field'''
bl_idname = 'ForceFieldNode'
bl_label = 'Force Field'
bl_icon = 'FORCE_FORCE'
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
return ntree.bl_idname == 'ForceFieldNodeTree'
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def draw_buttons(self, context, layout):
layout.context_pointer_set("node", self)
layout.template_ID(self, "id", new=ForceFieldNodesNew.bl_idname)
def compile(self, compiler):
pass
class SmokeSimNode(ObjectNodeBase, ObjectNode):
'''Smoke simulation'''
bl_idname = 'SmokeSimNode'
bl_label = 'Smoke Simulation'
bl_icon = 'MOD_SMOKE'
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
return ntree.bl_idname == 'SmokeSimNodeTree'
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def draw_buttons(self, context, layout):
layout.context_pointer_set("node", self)
layout.template_ID(self, "id", new=SmokeSimNodesNew.bl_idname)
def compile(self, compiler):
pass
class RigidBodyNode(ObjectNodeBase, ObjectNode):
'''Rigid body simulation'''
bl_idname = 'RigidBodyNode'
bl_label = 'Rigid Body Simulation'
bl_icon = 'PHYSICS'
bl_id_property_type = 'NODETREE'
def bl_id_property_poll(self, ntree):
return ntree.bl_idname == 'RigidBodyNodeTree'
def compile_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_compile_dependencies(depsnode)
def eval_dependencies(self, depsnode):
ntree = self.id
if ntree:
ntree.bvm_eval_dependencies(depsnode)
def draw_buttons(self, context, layout):
layout.context_pointer_set("node", self)
layout.template_ID(self, "id", new=RigidBodyNodesNew.bl_idname)
def compile(self, compiler):
pass
###############################################################################
class ObjectNodesNew(Operator):
"""Create new object node tree"""
bl_idname = "object_nodes.object_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
)
def execute(self, context):
return bpy.ops.node.new_node_tree(type='ObjectNodeTree', name="ObjectNodes")
class ObjectNodeEdit(Operator):
"""Open a node for editing"""
bl_idname = "object_nodes.node_edit"
bl_label = "Edit"
bl_options = {'REGISTER', 'UNDO'}
exit = BoolProperty(name="Exit", description="Exit current node tree", default=False)
@staticmethod
def get_node(context):
if hasattr(context, "node"):
return context.node
else:
return getattr(context, "active_node", None)
@classmethod
def poll(cls, context):
space = context.space_data
if space.type != 'NODE_EDITOR':
return False
treetype = getattr(bpy.types, space.tree_type)
if not issubclass(treetype, NodeTreeBase):
return False
return True
def execute(self, context):
space = context.space_data
node = self.get_node(context)
has_tree = node and node.id and isinstance(node.id, NodeTreeBase)
exit = self.exit or not has_tree
if exit:
space.path.pop()
else:
space.path.append(node.id, node)
return {'FINISHED'}
###############################################################################
keymaps = []
def register():
bpy.utils.register_module(__name__)
node_categories = [
ObjectNodeCategory("COMPONENTS", "Components", items=[
NodeItem("GeometryNode",
settings={"id": "bpy.types.OBJECT_NODES_OT_geometry_nodes_new.make_node_tree()"}),
NodeItem("ForceFieldNode",
settings={"id": "bpy.types.OBJECT_NODES_OT_forcefield_nodes_new.make_node_tree()"}),
NodeItem("InstancingNode",
settings={"id": "bpy.types.OBJECT_NODES_OT_instancing_nodes_new.make_node_tree()"}),
NodeItem("SmokeSimNode",
settings={"id": "bpy.types.OBJECT_NODES_OT_smokesim_nodes_new.make_node_tree()"}),
NodeItem("RigidBodyNode",
settings={"id": "bpy.types.OBJECT_NODES_OT_rigidbody_nodes_new.make_node_tree()"}),
]),
]
nodeitems_utils.register_node_categories("OBJECT_NODES", node_categories)
# create keymap
wm = bpy.context.window_manager
km = wm.keyconfigs.default.keymaps.new(name="Node Generic", space_type='NODE_EDITOR')
kmi = km.keymap_items.new(bpy.types.OBJECT_NODES_OT_node_edit.bl_idname, 'TAB', 'PRESS')
kmi.properties.exit = False
kmi = km.keymap_items.new(bpy.types.OBJECT_NODES_OT_node_edit.bl_idname, 'TAB', 'PRESS', ctrl=True)
kmi.properties.exit = True
keymaps.append(km)
def unregister():
nodeitems_utils.unregister_node_categories("OBJECT_NODES")
# remove keymap
wm = bpy.context.window_manager
for km in keymaps:
wm.keyconfigs.default.keymaps.remove(km)
keymaps.clear()
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,149 @@
# ##### 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
import nodeitems_utils
from bpy.types import PropertyGroup, Operator, ObjectNode, NodeTree, Node
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase, DynamicSocketListNode, enum_property_copy, enum_property_value_prop
import group_nodes
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class RigidBodyNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'RigidBodyNodeTree'
###############################################################################
class RigidBodyNodeTree(NodeTreeBase, NodeTree):
'''Smoke simulation nodes'''
bl_idname = 'RigidBodyNodeTree'
bl_label = 'Rigid Body Nodes'
bl_icon = 'PHYSICS'
# does not show up in the editor header
@classmethod
def poll(cls, context):
return False
def init_default(self):
#out = self.nodes.new(OutputNode.bl_idname)
#out.location = (100, 20)
pass
class RigidBodyNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'RigidBodyNodeTree'
###############################################################################
###############################################################################
class RigidBodyNodesNew(Operator):
"""Create new rigid body simulation node tree"""
bl_idname = "object_nodes.rigidbody_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
default="RigidBodyNodes",
)
@classmethod
def make_node_tree(cls, name="RigidBodyNodes"):
ntree = bpy.data.node_groups.new(name, RigidBodyNodeTree.bl_idname)
if ntree:
ntree.init_default()
return ntree
def execute(self, context):
node = getattr(context, "node", None)
ntree = self.make_node_tree(self.name)
if ntree is None:
return {'CANCELLED'}
if node:
node.id = ntree
return {'FINISHED'}
###############################################################################
def register():
bpy.utils.register_module(__name__)
gnode, ginput, goutput = group_nodes.make_node_group_types(
"RigidBody", RigidBodyNodeTree, RigidBodyNodeBase)
node_categories = [
RigidBodyNodeCategory("SMO_INPUT", "Input", items=[
NodeItem(ginput.bl_idname),
NodeItem("ObjectValueFloatNode"),
NodeItem("ObjectValueIntNode"),
NodeItem("ObjectValueVectorNode"),
NodeItem("ObjectValueColorNode"),
]),
RigidBodyNodeCategory("SMO_OUTPUT", "Output", items=[
NodeItem(goutput.bl_idname),
]),
RigidBodyNodeCategory("SMO_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
]),
RigidBodyNodeCategory("SMO_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
NodeItem("ObjectTranslationTransformNode"),
NodeItem("ObjectEulerTransformNode"),
NodeItem("ObjectAxisAngleTransformNode"),
NodeItem("ObjectScaleTransformNode"),
NodeItem("ObjectGetTranslationNode"),
NodeItem("ObjectGetEulerNode"),
NodeItem("ObjectGetAxisAngleNode"),
NodeItem("ObjectGetScaleNode"),
NodeItem("ObjectRandomNode"),
]),
RigidBodyNodeCategory("SMO_TEXTURE", "Texture", items=[
NodeItem("ObjectTextureCloudsNode"),
NodeItem("ObjectTextureDistNoiseNode"),
NodeItem("ObjectTextureMagicNode"),
NodeItem("ObjectTextureMarbleNode"),
NodeItem("ObjectTextureMusgraveNode"),
NodeItem("ObjectTextureStucciNode"),
NodeItem("ObjectTextureVoronoiNode"),
NodeItem("ObjectTextureWoodNode"),
]),
group_nodes.GroupNodeCategory("SMO", gnode, ginput, goutput),
]
nodeitems_utils.register_node_categories("RIGIDBODY_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("RIGIDBODY_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,333 @@
# ##### 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
import nodeitems_utils
from bpy.types import PropertyGroup, Operator, ObjectNode, NodeTree, Node
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
from common_nodes import NodeTreeBase, NodeBase, DynamicSocketListNode, enum_property_copy, enum_property_value_prop
import group_nodes
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class SmokeSimNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'SmokeSimNodeTree'
###############################################################################
class SmokeSimNodeTree(NodeTreeBase, NodeTree):
'''Smoke simulation nodes'''
bl_idname = 'SmokeSimNodeTree'
bl_label = 'SmokeSim Nodes'
bl_icon = 'MOD_SMOKE'
# does not show up in the editor header
@classmethod
def poll(cls, context):
return False
def init_default(self):
#out = self.nodes.new(OutputNode.bl_idname)
#out.location = (100, 20)
pass
class SmokeSimNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'SmokeSimNodeTree'
###############################################################################
# class SmokeStepNode(SmokeSimNodeBase, ObjectNode):
# '''Evolve the velocity field and marker particles of a smoke volume'''
# bl_idname = 'SmokeSimNode_Step'
# bl_label = 'Step'
# _advection_scheme_items = [
# ('SEMI_LAGRANGE', "Semi-Lagrange", "Semi-Lagrangian advection scheme"),
# ('MACCORMACK', "MacCormack", "MacCormack advection scheme"),
# ]
# advection_scheme = EnumProperty(name="Advection Scheme",
# description="Method of transport through the velocity field",
# items=_advection_scheme_items,
# default='MACCORMACK')
# def draw_buttons(self, context, layout):
# layout.prop(self, "advection_scheme")
# def init(self, context):
# s = self.inputs.new('NodeSocketInt', "Substeps")
# s.default_value = 1
# s = self.inputs.new('NodeSocketFloat', "Time Scale")
# s.default_value = 1.0
# self.inputs.new('NodeSocketFloat', "Density")
# self.inputs.new('NodeSocketVector', "Velocity")
# self.outputs.new('NodeSocketVector', "Velocity")
# def compile(self, compiler):
# pass
class TimeStepNode(SmokeSimNodeBase, ObjectNode):
'''Perform a smoke simulation time step'''
bl_idname = 'SmokeSimNode_TimeStep'
bl_label = 'Time Step'
def init(self, context):
s = self.inputs.new('NodeSocketInt', "Substeps")
s.default_value = 1
s = self.inputs.new('NodeSocketFloat', "Time Scale")
s.default_value = 1.0
self.inputs.new('NodeSocketFloat', "Density")
self.inputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class StateNode(SmokeSimNodeBase, ObjectNode):
'''State of the smoke simulation data'''
bl_idname = 'SmokeSimNode_State'
bl_label = 'State'
def init(self, context):
self.outputs.new('NodeSocketFloat', "Density")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class ExtForcesNode(SmokeSimNodeBase, ObjectNode):
'''Apply external forces to the velocity field'''
bl_idname = 'SmokeSimNode_ExtForces'
bl_label = 'External Forces'
bl_id_property_type = 'GROUP'
def draw_buttons(self, context, layout):
layout.template_ID(self, "id")
def init(self, context):
self.inputs.new('NodeSocketVector', "Velocity")
s = self.inputs.new('NodeSocketFloat', "Influence")
s.default_value = 1.0
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class AdvectNode(SmokeSimNodeBase, ObjectNode):
'''Advect grid data based on velocity'''
bl_idname = 'SmokeSimNode_Advect'
bl_label = 'Advect'
_mode_items = [
('SEMI_LAGRANGE', "Semi-Lagrange", "Semi-Lagrangian advection scheme"),
('MACCORMACK', "MacCormack", "MacCormack advection scheme"),
]
mode = EnumProperty(name="Mode",
description="Advection scheme",
items=_mode_items,
default='MACCORMACK')
def draw_buttons(self, context, layout):
layout.prop(self, "mode")
def init(self, context):
self.inputs.new('AnySocket', "Data")
self.inputs.new('NodeSocketVector', "Velocity")
self.outputs.new('AnySocket', "Advected Data")
def compile(self, compiler):
pass
class DiffusionNode(SmokeSimNodeBase, ObjectNode):
'''Apply a viscous diffusion step to a velocity grid'''
bl_idname = 'SmokeSimNode_Diffusion'
bl_label = 'Diffusion'
def init(self, context):
self.inputs.new('NodeSocketVector', "Velocity")
self.inputs.new('NodeSocketFloat', "Viscosity")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class ProjectionNode(SmokeSimNodeBase, ObjectNode):
'''Make a velocity field divergence-free by projection'''
bl_idname = 'SmokeSimNode_Projection'
bl_label = 'Projection'
def init(self, context):
self.inputs.new('NodeSocketVector', "Velocity")
self.outputs.new('NodeSocketFloat', "Pressure")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class ExtrapolateCellsNode(SmokeSimNodeBase, ObjectNode):
'''Extrapolate velocity grid into inactive cells'''
bl_idname = 'SmokeSimNode_ExtrapolateCells'
bl_label = 'Extrapolate Cells'
def init(self, context):
self.inputs.new('NodeSocketVector', "Velocity")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class ParticleGridsNode(SmokeSimNodeBase, ObjectNode):
'''Calculate grid values from particles'''
bl_idname = 'SmokeSimNode_ParticleGrids'
bl_label = 'Particle Grids'
def init(self, context):
self.inputs.new('GeometrySocket', "Particles")
self.outputs.new('NodeSocketFloat', "Density")
self.outputs.new('NodeSocketVector', "Velocity")
def compile(self, compiler):
pass
class MoveMarkerParticlesNode(SmokeSimNodeBase, ObjectNode):
'''Move marker particles through the velocity grid'''
bl_idname = 'SmokeSimNode_MoveMarkerParticles'
bl_label = 'Move Marker Particles'
def init(self, context):
self.inputs.new('GeometrySocket', "Particles")
self.inputs.new('NodeSocketVector', "Velocity")
self.outputs.new('GeometrySocket', "Particles")
def compile(self, compiler):
pass
###############################################################################
class SmokeSimNodesNew(Operator):
"""Create new smoke simulation node tree"""
bl_idname = "object_nodes.smokesim_nodes_new"
bl_label = "New"
bl_options = {'REGISTER', 'UNDO'}
name = StringProperty(
name="Name",
default="SmokeSimNodes",
)
@classmethod
def make_node_tree(cls, name="SmokeSimNodes"):
ntree = bpy.data.node_groups.new(name, SmokeSimNodeTree.bl_idname)
if ntree:
ntree.init_default()
return ntree
def execute(self, context):
node = getattr(context, "node", None)
ntree = self.make_node_tree(self.name)
if ntree is None:
return {'CANCELLED'}
if node:
node.id = ntree
return {'FINISHED'}
###############################################################################
def register():
bpy.utils.register_module(__name__)
gnode, ginput, goutput = group_nodes.make_node_group_types(
"SmokeSim", SmokeSimNodeTree, SmokeSimNodeBase)
node_categories = [
SmokeSimNodeCategory("SMO_INPUT", "Input", items=[
NodeItem("SmokeSimNode_State"),
NodeItem(ginput.bl_idname),
NodeItem("ObjectValueFloatNode"),
NodeItem("ObjectValueIntNode"),
NodeItem("ObjectValueVectorNode"),
NodeItem("ObjectValueColorNode"),
]),
SmokeSimNodeCategory("SMO_OUTPUT", "Output", items=[
NodeItem("SmokeSimNode_TimeStep"),
NodeItem(goutput.bl_idname),
]),
SmokeSimNodeCategory("SMO_VOLUME", "Volume", items=[
NodeItem("SmokeSimNode_ExtForces"),
NodeItem("SmokeSimNode_Advect"),
NodeItem("SmokeSimNode_Diffusion"),
NodeItem("SmokeSimNode_Projection"),
NodeItem("SmokeSimNode_ExtrapolateCells"),
NodeItem("SmokeSimNode_ParticleGrids"),
NodeItem("SmokeSimNode_MoveMarkerParticles"),
]),
SmokeSimNodeCategory("SMO_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
]),
SmokeSimNodeCategory("SMO_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
NodeItem("ObjectTranslationTransformNode"),
NodeItem("ObjectEulerTransformNode"),
NodeItem("ObjectAxisAngleTransformNode"),
NodeItem("ObjectScaleTransformNode"),
NodeItem("ObjectGetTranslationNode"),
NodeItem("ObjectGetEulerNode"),
NodeItem("ObjectGetAxisAngleNode"),
NodeItem("ObjectGetScaleNode"),
NodeItem("ObjectRandomNode"),
]),
SmokeSimNodeCategory("SMO_TEXTURE", "Texture", items=[
NodeItem("ObjectTextureCloudsNode"),
NodeItem("ObjectTextureDistNoiseNode"),
NodeItem("ObjectTextureMagicNode"),
NodeItem("ObjectTextureMarbleNode"),
NodeItem("ObjectTextureMusgraveNode"),
NodeItem("ObjectTextureStucciNode"),
NodeItem("ObjectTextureVoronoiNode"),
NodeItem("ObjectTextureWoodNode"),
]),
group_nodes.GroupNodeCategory("SMO", gnode, ginput, goutput),
]
nodeitems_utils.register_node_categories("SMOKESIM_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("SMOKESIM_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,219 @@
# ##### 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 NodeSocket
from bpy.props import *
###############################################################################
# Socket Types
class TransformSocket(NodeSocket):
'''Affine 3D transformation'''
bl_idname = 'TransformSocket'
bl_label = 'Transform'
def draw(self, context, layout, node, text):
layout.label(text)
def draw_color(self, context, node):
return (0.59, 0.00, 0.67, 1.00)
class GeometrySocket(NodeSocket):
'''Geometry data socket'''
bl_idname = 'GeometrySocket'
bl_label = 'Geometry'
is_placeholder = BoolProperty(name="Is Placeholder",
default=False)
def draw(self, context, layout, node, text):
layout.label(text)
def draw_color(self, context, node):
alpha = 0.4 if self.is_placeholder else 1.0
return (1.0, 0.4, 0.216, alpha)
class DupliSocket(NodeSocket):
'''Dupli instances socket'''
bl_idname = 'DupliSocket'
bl_label = 'Duplis'
is_placeholder = BoolProperty(name="Is Placeholder",
default=False)
def draw(self, context, layout, node, text):
layout.label(text)
def draw_color(self, context, node):
alpha = 0.4 if self.is_placeholder else 1.0
return (1.0, 0.4, 0.216, alpha)
class AnySocket(NodeSocket):
'''Any type socket'''
bl_idname = 'AnySocket'
bl_label = 'Any'
is_placeholder = BoolProperty(name="Is Placeholder",
default=False)
def draw(self, context, layout, node, text):
layout.label(text)
def draw_color(self, context, node):
alpha = 0.4 if self.is_placeholder else 1.0
return (0.95, 0.95, 0.95, alpha)
###############################################################################
# combined info for each socket type:
# identifier, UI name, description, bvm type, RNA struct type
_socket_type_info = [
("FLOAT", "Float", "Floating point number", 'FLOAT', bpy.types.NodeSocketFloat),
("INT", "Int", "Integer number", 'INT', bpy.types.NodeSocketInt),
("VECTOR", "Vector", "3D vector", 'FLOAT3', bpy.types.NodeSocketVector),
("COLOR", "Color", "RGBA color", 'FLOAT4', bpy.types.NodeSocketColor),
("MESH", "Mesh", "Mesh data", 'MESH', GeometrySocket),
("DUPLIS", "Duplis", "Dupli instances", 'DUPLIS', DupliSocket),
("TRANSFORM", "Transform", "Affine transformation", 'MATRIX44', TransformSocket),
]
socket_type_items = [(s[0], s[1], s[2], 0, i) for i,s in enumerate(_socket_type_info)]
def socket_type_to_rna(socket_type):
for s in _socket_type_info:
if s[0] == socket_type:
return s[4]
return None
def socket_type_to_bvm_type(socket_type):
for s in _socket_type_info:
if s[0] == socket_type:
return s[3]
return ''
def rna_to_socket_type(cls):
for s in _socket_type_info:
if issubclass(cls, s[4]):
return s[0]
return ''
def rna_to_bvm_type(cls):
for s in _socket_type_info:
if issubclass(cls, s[4]):
return s[3]
return ''
# determines if a conversion is necessary and possible
# and returns a new input socket to link
def convert_sockets(compiler, from_socket, to_socket):
from_type = from_socket.typedesc.base_type
to_type = to_socket.typedesc.base_type
if from_type == to_type:
return from_socket
def int_to_float(from_socket):
node = compiler.add_node("INT_TO_FLOAT")
compiler.link(from_socket, node.inputs[0])
return node.outputs[0]
def float_to_int(from_socket):
node = compiler.add_node("FLOAT_TO_INT")
compiler.link(from_socket, node.inputs[0])
return node.outputs[0]
def get_elem_float3(from_socket, index):
node = compiler.add_node("GET_ELEM_FLOAT3")
node.inputs[0].set_value(index)
compiler.link(from_socket, node.inputs[1])
return node.outputs[0]
def get_elem_float4(from_socket, index):
node = compiler.add_node("GET_ELEM_FLOAT4")
node.inputs[0].set_value(index)
compiler.link(from_socket, node.inputs[1])
return node.outputs[0]
def set_float3(from_socket):
node = compiler.add_node("SET_FLOAT3")
compiler.link(from_socket, node.inputs[0])
compiler.link(from_socket, node.inputs[1])
compiler.link(from_socket, node.inputs[2])
return node.outputs[0]
def set_float4(from_socket):
node = compiler.add_node("SET_FLOAT4")
compiler.link(from_socket, node.inputs[0])
compiler.link(from_socket, node.inputs[1])
compiler.link(from_socket, node.inputs[2])
compiler.link(from_socket, node.inputs[3])
return node.outputs[0]
def float3_to_float4(from_socket):
node = compiler.add_node("SET_FLOAT4")
compiler.link(get_elem_float3(from_socket, 0), node.inputs[0])
compiler.link(get_elem_float3(from_socket, 1), node.inputs[1])
compiler.link(get_elem_float3(from_socket, 2), node.inputs[2])
node.inputs[3].set_value(1.0)
return node.outputs[0]
def float4_to_float3(from_socket):
node = compiler.add_node("SET_FLOAT3")
compiler.link(get_elem_float4(from_socket, 0), node.inputs[0])
compiler.link(get_elem_float4(from_socket, 1), node.inputs[1])
compiler.link(get_elem_float4(from_socket, 2), node.inputs[2])
return node.outputs[0]
if to_type == 'FLOAT':
if from_type == 'INT':
return int_to_float(from_socket)
elif from_type == 'FLOAT3':
return get_elem_float3(from_socket, 0)
elif from_type == 'FLOAT4':
return get_elem_float4(from_socket, 0)
elif to_type == 'FLOAT3':
if from_type == 'FLOAT':
return set_float3(from_socket)
elif from_type == 'FLOAT4':
return float4_to_float3(from_socket)
elif from_type == 'INT':
return set_float3(int_to_float(from_socket))
elif to_type == 'FLOAT4':
if from_type == 'FLOAT':
return set_float4(from_socket)
elif from_type == 'FLOAT3':
return float3_to_float4(from_socket)
elif from_type == 'INT':
return set_float4(int_to_float(from_socket))
elif to_type == 'INT':
if from_type == 'FLOAT':
return float_to_int(from_socket)
if from_type == 'FLOAT3':
return float_to_int(get_elem_float3(from_socket, 0))
if from_type == 'FLOAT4':
return float_to_int(get_elem_float4(from_socket, 0))
elif to_type == 'MATRIX44':
pass
###############################################################################
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)

View File

@@ -0,0 +1,240 @@
# ##### 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
import nodeitems_utils
from bpy.types import Operator, ObjectNode, NodeTree, Node
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from common_nodes import NodeTreeBase, NodeBase, DynamicSocketListNode, enum_property_copy, enum_property_value_prop
import group_nodes
###############################################################################
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class TextureNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'TextureNodeTree'
###############################################################################
class TextureNodeTree(NodeTreeBase, NodeTree):
'''Texture nodes'''
bl_idname = 'TextureNodeTree'
bl_label = 'Texture Nodes'
bl_icon = 'TEXTURE'
@classmethod
def get_from_context(cls, context):
space = context.space_data
ntree = None
id_owner = None
id_from = None
if (space.texture_type == 'OBJECT'):
ob = context.active_object
if ob:
if ob.type == 'LAMP':
id_from = ob.data
id_owner = id_from.active_texture
else:
id_from = ob.active_material
ma = id_from.active_node_material if id_from else None
if not ma:
ma = id_from
id_owner = ma.active_texture if ma else None
ntree = id_owner.node_tree if id_owner else None
elif (space.texture_type == 'WORLD'):
scene = context.scene
id_from = scene.world
id_owner = id_from.active_texture if id_from else None
ntree = id_owner.node_tree if id_owner else None
elif (space.texture_type == 'BRUSH'):
ob = context.active_object
if ob and ob.mode == 'SCULPT':
id_from = scene.tool_settings.sculpt.brush
else:
id_from = scene.tool_settings.image_paint.brush
id_owner = id_from.texture if id_from else None
ntree = id_owner.node_tree if id_owner else None
elif (space.texture_type == 'LINESTYLE'):
rlayer = context.scene.render.layers.active
lineset = rlayer.freestyle.linesets.active if rlayer else None
id_from = lineset.linestyle if lineset else None
id_owner = id_from.active_texture if id_from else None
ntree = id_owner.node_tree if id_owner else None
return ntree, id_owner, id_from
def init_default(self):
out = self.nodes.new(TextureOutputNode.bl_idname)
out.location = (0, 20)
###############################################################################
class TextureNodeBase(NodeBase):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'TextureNodeTree'
class TextureOutputNode(TextureNodeBase, ObjectNode, DynamicSocketListNode):
'''Texture output'''
bl_idname = 'TextureOutputNode'
bl_label = 'Output'
def init(self, context):
s = self.inputs.new('NodeSocketColor', "Color")
s.default_value = (1, 1, 1, 1)
s = self.inputs.new('NodeSocketVector', "Normal")
s.default_value = (0, 0, 1)
def compile(self, compiler):
compiler.map_input(0, compiler.graph_output("color"))
compiler.map_input(1, compiler.graph_output("normal"))
class TextureCoordinateNode(TextureNodeBase, ObjectNode):
'''Texture coordinate'''
bl_idname = 'TextureCoordinateNode'
bl_label = 'Coordinate'
def init(self, context):
self.outputs.new('NodeSocketVector', "Location")
def compile(self, compiler):
compiler.map_output(0, compiler.graph_input("texture.co"))
class TextureGetDerivativeNode(TextureNodeBase, ObjectNode):
'''Get a partial derivative of a value'''
bl_idname = 'TextureGetDerivativeNode'
bl_label = 'Get Derivative'
_variable_items = [
('X', 'x', '', 'NONE', 0),
('Y', 'y', '', 'NONE', 1),
]
variable = EnumProperty(name="Variable",
description="Get derivative wrt. this variable",
items=_variable_items)
@property
def variable_index(self):
return self.bl_rna.properties['variable'].enum_items_static[self.variable].value
def draw_buttons(self, context, layout):
layout.prop(self, "variable")
def init(self, context):
self.inputs.new('NodeSocketFloat', "Value")
self.inputs.new('NodeSocketVector', "Value")
self.inputs.new('NodeSocketColor', "Value")
self.outputs.new('NodeSocketFloat', "Derivative")
self.outputs.new('NodeSocketVector', "Derivative")
self.outputs.new('NodeSocketColor', "Derivative")
def compile(self, compiler):
node = compiler.add_node("GET_DERIVATIVE_FLOAT")
node.inputs[0].set_value(self.variable_index)
compiler.map_input(0, node.inputs[1])
compiler.map_output(0, node.outputs[0])
node = compiler.add_node("GET_DERIVATIVE_FLOAT3")
node.inputs[0].set_value(self.variable_index)
compiler.map_input(1, node.inputs[1])
compiler.map_output(1, node.outputs[0])
node = compiler.add_node("GET_DERIVATIVE_FLOAT4")
node.inputs[0].set_value(self.variable_index)
compiler.map_input(2, node.inputs[1])
compiler.map_output(2, node.outputs[0])
###############################################################################
def register():
bpy.utils.register_module(__name__)
gnode, ginput, goutput = group_nodes.make_node_group_types(
"Texture", TextureNodeTree, TextureNodeBase)
node_categories = [
TextureNodeCategory("TEX_INPUT", "Input", items=[
NodeItem("TextureCoordinateNode"),
NodeItem(ginput.bl_idname),
NodeItem("ObjectValueFloatNode"),
NodeItem("ObjectValueIntNode"),
NodeItem("ObjectValueVectorNode"),
NodeItem("ObjectValueColorNode"),
NodeItem("ImageSampleNode"),
]),
TextureNodeCategory("TEX_OUTPUT", "Output", items=[
NodeItem("TextureOutputNode"),
NodeItem(goutput.bl_idname),
]),
TextureNodeCategory("TEX_CONVERTER", "Converter", items=[
NodeItem("ObjectSeparateVectorNode"),
NodeItem("ObjectCombineVectorNode"),
NodeItem("ObjectSeparateMatrixNode"),
NodeItem("ObjectCombineMatrixNode"),
NodeItem("TextureGetDerivativeNode"),
]),
TextureNodeCategory("TEX_MATH", "Math", items=[
NodeItem("ObjectMathNode"),
NodeItem("ObjectVectorMathNode"),
NodeItem("ObjectMatrixMathNode"),
NodeItem("ObjectTransformVectorNode"),
NodeItem("ObjectTranslationTransformNode"),
NodeItem("ObjectEulerTransformNode"),
NodeItem("ObjectAxisAngleTransformNode"),
NodeItem("ObjectScaleTransformNode"),
NodeItem("ObjectGetTranslationNode"),
NodeItem("ObjectGetEulerNode"),
NodeItem("ObjectGetAxisAngleNode"),
NodeItem("ObjectGetScaleNode"),
NodeItem("ObjectRandomNode"),
]),
TextureNodeCategory("TEX_TEXTURE", "Texture", items=[
NodeItem("ImageSampleNode"),
NodeItem("ObjectTextureCloudsNode"),
NodeItem("ObjectTextureDistNoiseNode"),
NodeItem("ObjectTextureGaborNoiseNode"),
NodeItem("ObjectTextureMagicNode"),
NodeItem("ObjectTextureMarbleNode"),
NodeItem("ObjectTextureMusgraveNode"),
NodeItem("ObjectTextureStucciNode"),
NodeItem("ObjectTextureVoronoiNode"),
NodeItem("ObjectTextureWoodNode"),
]),
group_nodes.GroupNodeCategory("TEX", gnode, ginput, goutput),
]
nodeitems_utils.register_node_categories("TEXTURE_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("TEXTURE_NODES")
bpy.utils.unregister_module(__name__)

View File

@@ -37,6 +37,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
ob = context.object
layout.template_ID(ob, "node_tree", new="object_nodes.object_nodes_new")
layout.separator()
layout.operator_menu_enum("object.modifier_add", "type")
for md in ob.modifiers:

View File

@@ -185,9 +185,7 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, Panel):
if tex:
split = layout.split(percentage=0.2)
if tex.use_nodes:
if slot:
split.label(text="Output:")
split.prop(slot, "output_node", text="")
pass
else:
split.label(text="Type:")
split.prop(tex, "type", text="")
@@ -218,9 +216,7 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, Panel):
if tex:
split = layout.split(percentage=0.2)
if tex.use_nodes:
if slot:
split.label(text="Output:")
split.prop(slot, "output_node", text="")
pass
else:
split.label(text="Type:")
split.prop(tex, "type", text="")

View File

@@ -53,12 +53,6 @@ class ShaderOldNodeCategory(SortedNodeCategory):
not context.scene.render.use_shading_nodes)
class TextureNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'TextureNodeTree'
# menu entry for node group tools
def group_tools_draw(self, layout, context):
layout.operator("node.group_make")
@@ -69,11 +63,10 @@ def group_tools_draw(self, layout, context):
node_tree_group_type = {
'CompositorNodeTree': 'CompositorNodeGroup',
'ShaderNodeTree': 'ShaderNodeGroup',
'TextureNodeTree': 'TextureNodeGroup',
}
# generic node group items generator for shader, compositor and texture node groups
# generic node group items generator for shader and compositor node groups
def node_group_items(context):
if context is None:
return
@@ -404,75 +397,15 @@ compositor_node_categories = [
]),
]
texture_node_categories = [
# Texture Nodes
TextureNodeCategory("TEX_INPUT", "Input", items=[
NodeItem("TextureNodeCurveTime"),
NodeItem("TextureNodeCoordinates"),
NodeItem("TextureNodeTexture"),
NodeItem("TextureNodeImage"),
NodeItem("NodeGroupInput", poll=group_input_output_item_poll),
]),
TextureNodeCategory("TEX_OUTPUT", "Output", items=[
NodeItem("TextureNodeOutput"),
NodeItem("TextureNodeViewer"),
NodeItem("NodeGroupOutput", poll=group_input_output_item_poll),
]),
TextureNodeCategory("TEX_OP_COLOR", "Color", items=[
NodeItem("TextureNodeMixRGB"),
NodeItem("TextureNodeCurveRGB"),
NodeItem("TextureNodeInvert"),
NodeItem("TextureNodeHueSaturation"),
NodeItem("TextureNodeCompose"),
NodeItem("TextureNodeDecompose"),
]),
TextureNodeCategory("TEX_PATTERN", "Pattern", items=[
NodeItem("TextureNodeChecker"),
NodeItem("TextureNodeBricks"),
]),
TextureNodeCategory("TEX_TEXTURE", "Textures", items=[
NodeItem("TextureNodeTexNoise"),
NodeItem("TextureNodeTexDistNoise"),
NodeItem("TextureNodeTexClouds"),
NodeItem("TextureNodeTexBlend"),
NodeItem("TextureNodeTexVoronoi"),
NodeItem("TextureNodeTexMagic"),
NodeItem("TextureNodeTexMarble"),
NodeItem("TextureNodeTexWood"),
NodeItem("TextureNodeTexMusgrave"),
NodeItem("TextureNodeTexStucci"),
]),
TextureNodeCategory("TEX_CONVERTOR", "Converter", items=[
NodeItem("TextureNodeMath"),
NodeItem("TextureNodeValToRGB"),
NodeItem("TextureNodeRGBToBW"),
NodeItem("TextureNodeValToNor"),
NodeItem("TextureNodeDistance"),
]),
TextureNodeCategory("TEX_DISTORT", "Distort", items=[
NodeItem("TextureNodeScale"),
NodeItem("TextureNodeTranslate"),
NodeItem("TextureNodeRotate"),
NodeItem("TextureNodeAt"),
]),
TextureNodeCategory("TEX_GROUP", "Group", items=node_group_items),
TextureNodeCategory("TEX_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
]
def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
def unregister():
nodeitems_utils.unregister_node_categories('SHADER')
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
if __name__ == "__main__":

View File

@@ -104,6 +104,7 @@ add_subdirectory(render)
add_subdirectory(blenfont)
add_subdirectory(blentranslation)
add_subdirectory(blenloader)
add_subdirectory(blenvm)
add_subdirectory(depsgraph)
add_subdirectory(ikplugin)
add_subdirectory(physics)

View File

@@ -65,6 +65,14 @@ int where_on_path(struct Object *ob, float ctime, float vec[4], float dir[3], fl
/* ---------------------------------------------------- */
/* Dupli-Geometry */
/* opaque API for storing dupli instances */
struct DupliContainer;
/* API wrapper for make_dupli using the DupliContainer type */
void BKE_dupli_add_instance(struct DupliContainer *cont,
struct Object *ob, float mat[4][4], int index,
bool animated, bool hide, bool recursive);
struct ListBase *object_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob, bool update);
struct ListBase *object_duplilist(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob);
void free_object_duplilist(struct ListBase *lb);

View File

@@ -105,15 +105,24 @@ typedef struct EffectorCache {
float guide_loc[4], guide_dir[3], guide_radius;
float velocity[3];
struct BVMFunction *function;
float frame;
int flag;
} EffectorCache;
typedef struct EffectorContext {
ListBase effectors;
struct BVMEvalGlobals *eval_globals;
} EffectorContext;
void free_partdeflect(struct PartDeflect *pd);
struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc);
void pdEndEffectors(struct ListBase **effectors);
void pdPrecalculateEffectors(struct ListBase *effectors);
void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse);
struct EffectorContext *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc);
void pdEndEffectors(struct EffectorContext *effctx);
void pdPrecalculateEffectors(struct EffectorContext *effctx);
void pdDoEffectors(struct EffectorContext *effctx, struct ListBase *colliders, struct EffectorWeights *weights,
struct EffectedPoint *point, float *force, float *impulse);
void pd_point_from_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, struct EffectedPoint *point);
void pd_point_from_loc(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);

View File

@@ -157,6 +157,7 @@ typedef struct bNodeType {
bNodeSocketTemplate *inputs, *outputs;
char storagename[64]; /* struct name for DNA */
int id_property_type; /* static type of the ID property, if used */
/* Main draw function for the node */
void (*draw_nodetype)(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode,
@@ -184,6 +185,8 @@ typedef struct bNodeType {
/// Optional tweak area polling (for grabbing).
int (*tweak_area_func)(struct bNode *node, int x, int y);
/// Return true when the node is used for final results (connected to output).
bool (*is_used)(struct bNodeTree *ntree, struct bNode *node);
/// Called when the node is updated in the editor.
void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node);
/// Check and update if internal ID data has changed.
@@ -461,6 +464,7 @@ void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
bool nodeLinkIsHidden(struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
bool nodeIsUsed(struct bNodeTree *ntree, struct bNode *node);
void nodeToView(struct bNode *node, float x, float y, float *rx, float *ry);
void nodeFromView(struct bNode *node, float x, float y, float *rx, float *ry);
@@ -995,45 +999,9 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
/** \name Texture Nodes
*/
struct TexResult;
#define TEX_NODE_OUTPUT 401
#define TEX_NODE_CHECKER 402
#define TEX_NODE_TEXTURE 403
#define TEX_NODE_BRICKS 404
#define TEX_NODE_MATH 405
#define TEX_NODE_MIX_RGB 406
#define TEX_NODE_RGBTOBW 407
#define TEX_NODE_VALTORGB 408
#define TEX_NODE_IMAGE 409
#define TEX_NODE_CURVE_RGB 410
#define TEX_NODE_INVERT 411
#define TEX_NODE_HUE_SAT 412
#define TEX_NODE_CURVE_TIME 413
#define TEX_NODE_ROTATE 414
#define TEX_NODE_VIEWER 415
#define TEX_NODE_TRANSLATE 416
#define TEX_NODE_COORD 417
#define TEX_NODE_DISTANCE 418
#define TEX_NODE_COMPOSE 419
#define TEX_NODE_DECOMPOSE 420
#define TEX_NODE_VALTONOR 421
#define TEX_NODE_SCALE 422
#define TEX_NODE_AT 423
/* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */
#define TEX_NODE_PROC 500
#define TEX_NODE_PROC_MAX 600
/* API */
int ntreeTexTagAnimated(struct bNodeTree *ntree);
void ntreeTexCheckCyclics(struct bNodeTree *ntree);
struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree);
void ntreeTexEndExecTree(struct bNodeTreeExec *exec);
int ntreeTexExecTree(struct bNodeTree *ntree, struct TexResult *target,
float coord[3], float dxt[3], float dyt[3], int osatex, const short thread,
struct Tex *tex, short which_output, int cfra, int preview, struct ShadeInput *shi, struct MTex *mtex);
/** \} */
void init_nodesystem(void);

View File

@@ -62,6 +62,7 @@ struct RNG;
struct BVHTreeRay;
struct BVHTreeRayHit;
struct EdgeHash;
struct EffectorContext;
#define PARTICLE_COLLISION_MAX_COLLISIONS 10
@@ -334,8 +335,8 @@ void psys_find_parents(struct ParticleSimulationData *sim, const bool use_render
void psys_cache_paths(struct ParticleSimulationData *sim, float cfra, const bool use_render_params);
void psys_cache_edit_paths(struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra, const bool use_render_params);
void psys_cache_child_paths(struct ParticleSimulationData *sim, float cfra, const bool editupdate, const bool use_render_params);
int do_guides(struct ParticleSettings *part, struct ListBase *effectors, ParticleKey *state, int pa_num, float time);
void precalc_guides(struct ParticleSimulationData *sim, struct ListBase *effectors);
int do_guides(struct ParticleSettings *part, struct EffectorContext *effectors, ParticleKey *state, int pa_num, float time);
void precalc_guides(struct ParticleSimulationData *sim, struct EffectorContext *effectors);
float psys_get_timestep(struct ParticleSimulationData *sim);
float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *birthtime, float *dietime);
float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *pa_time);

View File

@@ -39,8 +39,10 @@ extern "C" {
struct bNode;
struct Brush;
struct BVMEvalGlobals;
struct ColorBand;
struct EnvMap;
struct EvaluationContext;
struct FreestyleLineStyle;
struct Lamp;
struct Main;
@@ -135,7 +137,15 @@ bool BKE_texture_is_image_user(const struct Tex *tex);
void BKE_texture_get_value(
const struct Scene *scene, struct Tex *texture,
float *tex_co, struct TexResult *texres, bool use_color_management);
const float *tex_co, struct TexResult *texres, bool use_color_management);
void BKE_texture_get_value_ex(
struct BVMEvalGlobals *globals,
const struct Scene *scene, struct Tex *texture,
const float *tex_co, const float *tex_dx, const float *tex_dy,
struct TexResult *texres, struct TexResult *texres_dx, struct TexResult *texres_dy,
bool use_color_management);
void BKE_texture_invalidate(struct EvaluationContext *eval_ctx, struct Tex *tex);
#ifdef __cplusplus
}

View File

@@ -29,6 +29,7 @@ set(INC
../blenlib
../blenloader
../blentranslation
../blenvm
../depsgraph
../gpu
../ikplugin

View File

@@ -40,6 +40,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -75,6 +76,8 @@ static DerivedMesh *navmesh_dm_createNavMeshForVisualization(DerivedMesh *dm);
#include "BLI_sys_types.h" /* for intptr_t support */
#include "BVM_api.h"
#include "GPU_buffers.h"
#include "GPU_glew.h"
#include "GPU_shader.h"
@@ -1719,6 +1722,35 @@ static void dm_ensure_display_normals(DerivedMesh *dm)
}
}
/* XXX this should replace modifier stack eventually */
static DerivedMesh *mesh_calc_modifier_nodes(Scene *UNUSED(scene), Object *ob, bNodeTree *ntree)
{
Mesh *me = ob->data;
DerivedMesh *dm, *result;
struct BVMFunction *fn = BVM_gen_modifier_function_llvm(ntree, true);
if (fn) {
struct BVMEvalGlobals *globals = BVM_globals_create();
BVM_globals_add_nodetree_relations(globals, ntree);
struct BVMEvalContext *context = BVM_context_create();
dm = BVM_eval_modifier_llvm(globals, context, fn, ob, me);
BVM_context_free(context);
BVM_globals_free(globals);
BVM_function_llvm_release(fn);
}
/* XXX this is stupid, but currently required because of
* the unreliability of dm->needsFree ...
* This flag gets set in places to force freeing of meshes, can't expect this to work
*/
result = CDDM_copy(dm);
dm->release(dm);
return result;
}
/**
* new value for useDeform -1 (hack for the gameengine):
*
@@ -1779,6 +1811,38 @@ static void mesh_calc_modifiers(
if (useDeform)
deform_app_flags |= MOD_APPLY_USECACHE;
if (ob->nodetree) {
bNodeTree *geotree = NULL;
DerivedMesh *node_dm = NULL;
/* XXX TODO not nice, we can potentially have multiple geometry
* subtrees and it's not clear yet how these would be combined.
* For the time being just select the first and expect it to be the only one ...
*/
{
bNode *node;
for (node = ob->nodetree->nodes.first; node; node = node->next) {
if (STREQ(node->idname, "GeometryNode")) {
geotree = (bNodeTree *)node->id;
break;
}
}
}
if (geotree) {
node_dm = mesh_calc_modifier_nodes(scene, ob, geotree);
DM_ensure_normals(node_dm);
}
else {
node_dm = CDDM_from_mesh(me);
}
*r_final = node_dm;
if (r_deform)
*r_deform = CDDM_copy(node_dm);
return;
}
if (!skipVirtualArmature) {
firstmd = modifiers_getVirtualModifierList(ob, &virtualModifierData);
}

View File

@@ -61,6 +61,8 @@
#include "BKE_screen.h"
#include "BKE_sequencer.h"
#include "BVM_api.h"
#include "RE_pipeline.h"
#include "RE_render_ext.h"
@@ -96,6 +98,7 @@ void BKE_blender_free(void)
IMB_moviecache_destruct();
free_nodesystem();
BVM_free();
}
void BKE_blender_globals_init(void)

View File

@@ -72,7 +72,7 @@ static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val,
BoidSettings *boids = bbd->part->boids;
BoidParticle *bpa = pa->boid;
EffectedPoint epoint;
ListBase *effectors = bbd->sim->psys->effectors;
EffectorContext *effectors = bbd->sim->psys->effectors;
EffectorCache *cur, *eff = NULL;
EffectorCache temp_eff;
EffectorData efd, cur_efd;
@@ -86,7 +86,7 @@ static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val,
pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
/* first find out goal/predator with highest priority */
if (effectors) for (cur = effectors->first; cur; cur=cur->next) {
if (effectors) for (cur = effectors->effectors.first; cur; cur=cur->next) {
Object *eob = cur->ob;
PartDeflect *pd = cur->pd;

View File

@@ -347,7 +347,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
{
ClothVertex *verts = NULL;
Cloth *cloth;
ListBase *effectors = NULL;
EffectorContext *effectors;
MVert *mvert;
unsigned int i = 0;
int ret = 0;
@@ -388,7 +388,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
// TIMEIT_END(cloth_step)
pdEndEffectors(&effectors);
pdEndEffectors(effectors);
// printf ( "%f\n", ( float ) tval() );

View File

@@ -84,6 +84,8 @@
#include "BKE_screen.h"
#include "BKE_tracking.h"
#include "BVM_api.h"
#include "GPU_buffers.h"
#include "atomic_ops.h"
@@ -763,7 +765,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
BoidRule *rule = NULL;
BoidState *state = NULL;
ParticleSettings *part = psys->part;
ListBase *effectors = NULL;
EffectorContext *effectors;
EffectorCache *eff;
if (part->adt) {
@@ -806,7 +808,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
if (effectors) {
for (eff = effectors->first; eff; eff = eff->next) {
for (eff = effectors->effectors.first; eff; eff = eff->next) {
if (eff->psys) {
node2 = dag_get_node(dag, eff->ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field");
@@ -814,7 +816,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
pdEndEffectors(&effectors);
pdEndEffectors(effectors);
if (part->boids) {
for (state = part->boids->states.first; state; state = state->next) {
@@ -2706,6 +2708,11 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
}
}
if (ELEM(idtype, ID_TE)) {
Tex *tex = (Tex *)id;
BVM_function_llvm_cache_remove(tex->nodetree);
}
if (idtype == ID_MC) {
MovieClip *clip = (MovieClip *) id;

View File

@@ -4537,7 +4537,7 @@ typedef struct DynamicPaintEffectData {
Scene *scene;
float *force;
ListBase *effectors;
EffectorContext *effectors;
const void *prevPoint;
const float eff_scale;
@@ -4567,9 +4567,8 @@ static void dynamic_paint_prepare_effect_cb(void *userdata, const int index)
Vec3f *realCoord = bData->realCoord;
Scene *scene = data->scene;
EffectorContext *effectors = data->effectors;
float *force = data->force;
ListBase *effectors = data->effectors;
float forc[3] = {0};
float vel[3] = {0};
@@ -4619,7 +4618,7 @@ static int dynamicPaint_prepareEffectStep(
/* Init force data if required */
if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true);
EffectorContext *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true);
/* allocate memory for force data (dir vector + strength) */
*force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces");
@@ -4638,7 +4637,7 @@ static int dynamicPaint_prepareEffectStep(
}
average_force /= sData->total_points;
}
pdEndEffectors(&effectors);
pdEndEffectors(effectors);
}
/* Get number of required steps using average point distance

View File

@@ -41,6 +41,7 @@
#include "DNA_group_types.h"
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
#include "DNA_particle_types.h"
@@ -66,11 +67,13 @@
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_smoke.h"
#include "BVM_api.h"
#include "RE_render_ext.h"
#include "RE_shader_ext.h"
@@ -145,7 +148,7 @@ void free_partdeflect(PartDeflect *pd)
MEM_freeN(pd);
}
static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSystem *psys, PartDeflect *pd)
static EffectorCache *new_effector_cache(EffectorContext *effctx, Scene *scene, Object *ob, ParticleSystem *psys, PartDeflect *pd)
{
EffectorCache *eff = MEM_callocN(sizeof(EffectorCache), "EffectorCache");
eff->scene = scene;
@@ -153,29 +156,25 @@ static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSyste
eff->psys = psys;
eff->pd = pd;
eff->frame = -1;
BLI_addtail(&effctx->effectors, eff);
return eff;
}
static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src)
static void add_object_to_effectors(EffectorContext *effctx, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src)
{
EffectorCache *eff = NULL;
if ( ob == ob_src || weights->weight[ob->pd->forcefield] == 0.0f )
return;
if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal )
return;
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
eff = new_effector_cache(scene, ob, NULL, ob->pd);
new_effector_cache(effctx, scene, ob, NULL, ob->pd);
/* make sure imat is up to date */
invert_m4_m4(ob->imat, ob->obmat);
BLI_addtail(*effectors, eff);
}
static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src)
static void add_particles_to_effectors(EffectorContext *effctx, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src)
{
ParticleSettings *part= psys->part;
@@ -186,27 +185,52 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec
return;
if ( part->pd && part->pd->forcefield && weights->weight[part->pd->forcefield] != 0.0f) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd));
new_effector_cache(effctx, scene, ob, psys, part->pd);
}
if (part->pd2 && part->pd2->forcefield && weights->weight[part->pd2->forcefield] != 0.0f) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd2));
new_effector_cache(effctx, scene, ob, psys, part->pd2);
}
}
static void add_object_nodes_to_effectors(EffectorContext *effctx, Scene *scene, EffectorWeights *UNUSED(weights), Object *ob, Object *ob_src)
{
if (ob == ob_src)
return;
if (ob->nodetree) {
bNode *node;
BVM_globals_add_object(effctx->eval_globals, BVM_get_id_key((ID *)ob), ob);
/* XXX TODO This is a placeholder for future component nodes design! */
for (node = ob->nodetree->nodes.first; node; node = node->next) {
if (STREQ(node->typeinfo->idname, "ForceFieldNode")) {
bNodeTree *ff_ntree = (bNodeTree *)node->id;
if (ff_ntree) {
BVM_globals_add_nodetree_relations(effctx->eval_globals, ff_ntree);
EffectorCache *eff = new_effector_cache(effctx, scene, ob, NULL, ob->pd);
eff->function = BVM_gen_forcefield_function_bvm(ff_ntree, true);
}
break;
}
}
}
}
/* returns ListBase handle with objects taking part in the effecting */
ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
EffectorWeights *weights, bool precalc)
EffectorContext *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
EffectorWeights *weights, bool precalc)
{
EffectorContext *effctx = MEM_callocN(sizeof(EffectorContext), "effector context");
Base *base;
unsigned int layer= ob_src->lay;
ListBase *effectors = NULL;
effctx->eval_globals = BVM_globals_create();
BVM_globals_add_object(effctx->eval_globals, BVM_get_id_key((ID *)ob_src), ob_src);
if (weights->group) {
GroupObject *go;
@@ -214,13 +238,15 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (go= weights->group->gobject.first; go; go= go->next) {
if ( (go->ob->lay & layer) ) {
if ( go->ob->pd && go->ob->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src);
add_object_to_effectors(effctx, scene, weights, go->ob, ob_src);
add_object_nodes_to_effectors(effctx, scene, weights, go->ob, ob_src);
if ( go->ob->particlesystem.first ) {
ParticleSystem *psys= go->ob->particlesystem.first;
for ( ; psys; psys=psys->next )
add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src);
add_particles_to_effectors(effctx, scene, weights, go->ob, psys, psys_src);
}
}
}
@@ -229,37 +255,43 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (base = scene->base.first; base; base= base->next) {
if ( (base->lay & layer) ) {
if ( base->object->pd && base->object->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, base->object, ob_src);
add_object_to_effectors(effctx, scene, weights, base->object, ob_src);
add_object_nodes_to_effectors(effctx, scene, weights, base->object, ob_src);
if ( base->object->particlesystem.first ) {
ParticleSystem *psys= base->object->particlesystem.first;
for ( ; psys; psys=psys->next )
add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src);
add_particles_to_effectors(effctx, scene, weights, base->object, psys, psys_src);
}
}
}
}
if (precalc)
pdPrecalculateEffectors(effectors);
pdPrecalculateEffectors(effctx);
return effectors;
return effctx;
}
void pdEndEffectors(ListBase **effectors)
void pdEndEffectors(EffectorContext *effctx)
{
if (*effectors) {
EffectorCache *eff = (*effectors)->first;
for (; eff; eff=eff->next) {
if (effctx) {
EffectorCache *eff = effctx->effectors.first;
for (; eff; eff = eff->next) {
if (eff->guide_data)
MEM_freeN(eff->guide_data);
if (eff->function)
BVM_function_bvm_release(eff->function);
}
BLI_freelistN(*effectors);
MEM_freeN(*effectors);
*effectors = NULL;
BLI_freelistN(&effctx->effectors);
if (effctx->eval_globals)
BVM_globals_free(effctx->eval_globals);
MEM_freeN(effctx);
}
}
@@ -303,13 +335,11 @@ static void precalculate_effector(EffectorCache *eff)
}
}
void pdPrecalculateEffectors(ListBase *effectors)
void pdPrecalculateEffectors(EffectorContext *effctx)
{
if (effectors) {
EffectorCache *eff = effectors->first;
for (; eff; eff=eff->next)
precalculate_effector(eff);
}
EffectorCache *eff = effctx->effectors.first;
for (; eff; eff = eff->next)
precalculate_effector(eff);
}
@@ -957,8 +987,8 @@ static void do_physical_effector(EffectorCache *eff, EffectorData *efd, Effected
/* -------- pdDoEffectors() --------
* generic force/speed system, now used for particles and softbodies
* effctx = compiled effector setup
* scene = scene where it runs in, for time and stuff
* lb = listbase with objects that take part in effecting
* opco = global coord, as input
* force = force accumulator
* speed = actual current speed which can be altered
@@ -968,7 +998,7 @@ static void do_physical_effector(EffectorCache *eff, EffectorData *efd, Effected
* flags = only used for softbody wind now
* guide = old speed of particle
*/
void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *weights, EffectedPoint *point, float *force, float *impulse)
void pdDoEffectors(struct EffectorContext *effctx, ListBase *colliders, EffectorWeights *weights, EffectedPoint *point, float *force, float *impulse)
{
/*
* Modifies the force on a particle according to its
@@ -987,16 +1017,21 @@ void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *we
EffectorData efd;
int p=0, tot = 1, step = 1;
struct BVMEvalContext *eval_context = BVM_context_create();
/* Cycle through collected objects, get total of (1/(gravity_strength * dist^gravity_power)) */
/* Check for min distance here? (yes would be cool to add that, ton) */
if (effectors) for (eff = effectors->first; eff; eff=eff->next) {
if (effctx) for (eff = effctx->effectors.first; eff; eff=eff->next) {
/* object effectors were fully checked to be OK to evaluate! */
get_effector_tot(eff, &efd, point, &tot, &p, &step);
for (; p<tot; p+=step) {
if (get_effector_data(eff, &efd, point, 0)) {
if (eff->function) {
BVM_eval_forcefield_bvm(effctx->eval_globals, eval_context, eff->function, eff->ob, point, force, impulse);
}
else if (get_effector_data(eff, &efd, point, 0)) {
efd.falloff= effector_falloff(eff, &efd, point, weights);
if (efd.falloff > 0.0f)
@@ -1027,6 +1062,8 @@ void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *we
}
}
}
BVM_context_free(eval_context);
}
/* ======== Simulation Debugging ======== */

View File

@@ -2544,17 +2544,6 @@ void BKE_image_walk_all_users(const Main *mainp, void *customdata,
if (tex->type == TEX_IMAGE && tex->ima) {
callback(tex->ima, &tex->iuser, customdata);
}
if (tex->nodetree) {
bNode *node;
for (node = tex->nodetree->nodes.first; node; node = node->next) {
if (node->id && node->type == TEX_NODE_IMAGE) {
Image *ima = (Image *)node->id;
ImageUser *iuser = node->storage;
callback(ima, iuser, customdata);
}
}
}
}
/* image window, compo node users */

View File

@@ -1089,6 +1089,16 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
}
}
bool nodeIsUsed(bNodeTree *ntree, bNode *node)
{
if (node->typeinfo->is_used)
return node->typeinfo->is_used(ntree, node);
else
return true;
}
void nodeToView(bNode *node, float x, float y, float *rx, float *ry)
{
if (node->parent) {
@@ -3627,56 +3637,6 @@ static void registerShaderNodes(void)
register_node_type_sh_tex_pointdensity();
}
static void registerTextureNodes(void)
{
register_node_type_tex_group();
register_node_type_tex_math();
register_node_type_tex_mix_rgb();
register_node_type_tex_valtorgb();
register_node_type_tex_rgbtobw();
register_node_type_tex_valtonor();
register_node_type_tex_curve_rgb();
register_node_type_tex_curve_time();
register_node_type_tex_invert();
register_node_type_tex_hue_sat();
register_node_type_tex_coord();
register_node_type_tex_distance();
register_node_type_tex_compose();
register_node_type_tex_decompose();
register_node_type_tex_output();
register_node_type_tex_viewer();
register_node_type_sh_script();
register_node_type_sh_tangent();
register_node_type_sh_normal_map();
register_node_type_sh_hair_info();
register_node_type_tex_checker();
register_node_type_tex_texture();
register_node_type_tex_bricks();
register_node_type_tex_image();
register_node_type_sh_bsdf_refraction();
register_node_type_sh_ambient_occlusion();
register_node_type_tex_rotate();
register_node_type_tex_translate();
register_node_type_tex_scale();
register_node_type_tex_at();
register_node_type_tex_proc_voronoi();
register_node_type_tex_proc_blend();
register_node_type_tex_proc_magic();
register_node_type_tex_proc_marble();
register_node_type_tex_proc_clouds();
register_node_type_tex_proc_wood();
register_node_type_tex_proc_musgrave();
register_node_type_tex_proc_noise();
register_node_type_tex_proc_stucci();
register_node_type_tex_proc_distnoise();
}
void init_nodesystem(void)
{
nodetreetypes_hash = BLI_ghash_str_new("nodetreetypes_hash gh");
@@ -3690,7 +3650,6 @@ void init_nodesystem(void)
register_node_tree_type_cmp();
register_node_tree_type_sh();
register_node_tree_type_tex();
register_node_type_frame();
register_node_type_reroute();
@@ -3699,7 +3658,6 @@ void init_nodesystem(void)
registerCompositNodes();
registerShaderNodes();
registerTextureNodes();
}
void free_nodesystem(void)

View File

@@ -44,6 +44,7 @@
#include "DNA_anim_types.h"
#include "DNA_group_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
@@ -62,6 +63,8 @@
#include "BKE_editmesh.h"
#include "BKE_anim.h"
#include "BVM_api.h"
#include "BLI_strict_flags.h"
@@ -87,8 +90,14 @@ typedef struct DupliContext {
ListBase *duplilist; /* legacy doubly-linked list */
} DupliContext;
static struct DupliContainer *get_dupli_container(const DupliContext *ctx)
{
/* note: DupliContainer is a dummy type, used only to clarify the API */
return (struct DupliContainer *)ctx;
}
typedef struct DupliGenerator {
short type; /* dupli type */
int type; /* dupli type */
void (*make_duplis)(const DupliContext *ctx);
} DupliGenerator;
@@ -1138,6 +1147,52 @@ const DupliGenerator gen_dupli_particles = {
make_duplis_particles /* make_duplis */
};
/* OB_DUPLINODES */
static void make_duplis_nodetree(struct bNodeTree *ntree, const DupliContext *dupctx)
{
struct BVMFunction *fn = BVM_gen_dupli_function_bvm(ntree, true);
if (fn) {
struct BVMEvalGlobals *globals = BVM_globals_create();
BVM_globals_add_nodetree_relations(globals, ntree);
struct BVMEvalContext *context = BVM_context_create();
BVM_eval_dupli_bvm(globals, context, fn, dupctx->object, get_dupli_container(dupctx));
BVM_context_free(context);
BVM_globals_free(globals);
BVM_function_bvm_release(fn);
}
}
static void make_duplis_nodes(const DupliContext *ctx)
{
Object *ob = ctx->object;
if (ob->nodetree) {
bNodeTree *duplitree = NULL;
{
bNode *node;
for (node = ob->nodetree->nodes.first; node; node = node->next) {
if (STREQ(node->idname, "InstancingNode")) {
duplitree = (bNodeTree *)node->id;
if (duplitree)
make_duplis_nodetree(duplitree, ctx);
}
}
}
}
}
const DupliGenerator gen_dupli_nodes = {
OB_DUPLINODES, /* type */
make_duplis_nodes /* make_duplis */
};
/* ------------- */
/* select dupli generator from given context */
@@ -1174,10 +1229,30 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
else if (transflag & OB_DUPLIGROUP) {
return &gen_dupli_group;
}
else if (transflag & OB_DUPLINODES) {
return &gen_dupli_nodes;
}
return NULL;
}
/* API wrapper for make_dupli using the DupliContainer type */
void BKE_dupli_add_instance(struct DupliContainer *cont,
Object *ob, float mat[4][4], int index,
bool animated, bool hide, bool recursive)
{
const DupliContext *ctx = (const DupliContext *)cont;
float dobmat[4][4], imat[4][4], space_mat[4][4];
mul_m4_m4m4(dobmat, ctx->object->obmat, mat);
invert_m4_m4(imat, ob->obmat);
mul_m4_m4m4(space_mat, dobmat, imat);
make_dupli(ctx, ob, dobmat, index, animated, hide);
if (recursive)
make_recursive_duplis(ctx, ob, space_mat, index, animated);
}
/* ---- ListBase dupli container implementation ---- */

View File

@@ -584,7 +584,7 @@ void psys_free(Object *ob, ParticleSystem *psys)
if (psys->fluid_springs)
MEM_freeN(psys->fluid_springs);
pdEndEffectors(&psys->effectors);
pdEndEffectors(psys->effectors);
if (psys->pdd) {
psys_free_pdd(psys);
@@ -1778,7 +1778,7 @@ extern void do_kink(ParticleKey *state, const float par_co[3], const float par_v
extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
void precalc_guides(ParticleSimulationData *sim, ListBase *effectors)
void precalc_guides(ParticleSimulationData *sim, EffectorContext *effectors)
{
EffectedPoint point;
ParticleKey state;
@@ -1800,7 +1800,7 @@ void precalc_guides(ParticleSimulationData *sim, ListBase *effectors)
pd_point_from_particle(sim, pa, &state, &point);
for (eff = effectors->first; eff; eff = eff->next) {
for (eff = effectors->effectors.first; eff; eff = eff->next) {
if (eff->pd->forcefield != PFIELD_GUIDE)
continue;
@@ -1819,7 +1819,7 @@ void precalc_guides(ParticleSimulationData *sim, ListBase *effectors)
}
}
int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, int index, float time)
int do_guides(ParticleSettings *part, EffectorContext *effectors, ParticleKey *state, int index, float time)
{
CurveMapping *clumpcurve = (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) ? part->clumpcurve : NULL;
CurveMapping *roughcurve = (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) ? part->roughcurve : NULL;
@@ -1833,7 +1833,7 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i
float guidetime, radius, weight, angle, totstrength = 0.0f;
float vec_to_point[3];
if (effectors) for (eff = effectors->first; eff; eff = eff->next) {
if (effectors) for (eff = effectors->effectors.first; eff; eff = eff->next) {
pd = eff->pd;
if (pd->forcefield != PFIELD_GUIDE)
@@ -2680,7 +2680,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_re
}
/* apply guide curves to path data */
if (sim->psys->effectors && (psys->part->flag & PART_CHILD_EFFECT) == 0) {
if ((psys->part->flag & PART_CHILD_EFFECT) == 0) {
for (k = 0, ca = cache[p]; k <= segments; k++, ca++)
/* ca is safe to cast, since only co and vel are used */
do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)segments);
@@ -3720,7 +3720,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *
mul_m4_v3(hairmat, state->co);
mul_mat3_m4_v3(hairmat, state->vel);
if (sim->psys->effectors && (part->flag & PART_CHILD_GUIDE) == 0) {
if ((part->flag & PART_CHILD_GUIDE) == 0) {
do_guides(sim->psys->part, sim->psys->effectors, state, p, state->time);
/* TODO: proper velocity handling */
}

View File

@@ -1293,9 +1293,9 @@ void psys_update_particle_tree(ParticleSystem *psys, float cfra)
static void psys_update_effectors(ParticleSimulationData *sim)
{
pdEndEffectors(&sim->psys->effectors);
pdEndEffectors(sim->psys->effectors);
sim->psys->effectors = pdInitEffectors(sim->scene, sim->ob, sim->psys,
sim->psys->part->effector_weights, true);
sim->psys->part->effector_weights, true);
precalc_guides(sim, sim->psys->effectors);
}

View File

@@ -1252,7 +1252,7 @@ static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *o
else if (rbo->type == RBO_TYPE_ACTIVE && ((ob->pd == NULL) || (ob->pd->forcefield == PFIELD_NULL))) {
EffectorWeights *effector_weights = rbw->effector_weights;
EffectedPoint epoint;
ListBase *effectors;
EffectorContext *effectors;
/* get effectors present in the group specified by effector_weights */
effectors = pdInitEffectors(scene, ob, NULL, effector_weights, true);
@@ -1282,7 +1282,7 @@ static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *o
printf("\tno forces to apply to '%s'\n", ob->id.name + 2);
/* cleanup */
pdEndEffectors(&effectors);
pdEndEffectors(effectors);
}
/* NOTE: passive objects don't need to be updated since they don't move */

View File

@@ -2396,7 +2396,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
typedef struct UpdateEffectorsData {
Scene *scene;
SmokeDomainSettings *sds;
ListBase *effectors;
EffectorContext *effectors;
float *density;
float *fuel;
@@ -2462,7 +2462,7 @@ static void update_effectors_task_cb(void *userdata, const int x)
static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, float UNUSED(dt))
{
ListBase *effectors;
EffectorContext *effectors;
/* make sure smoke flow influence is 0.0f */
sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f;
effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true);
@@ -2486,7 +2486,7 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds,
BLI_task_parallel_range(0, sds->res[0], &data, update_effectors_task_cb, true);
}
pdEndEffectors(&effectors);
pdEndEffectors(effectors);
}
static void step(Scene *scene, Object *ob, SmokeModifierData *smd, DerivedMesh *domain_dm, float fps)

View File

@@ -134,7 +134,7 @@ typedef struct SB_thread_context {
float timenow;
int ifirst;
int ilast;
ListBase *do_effector;
EffectorContext *do_effector;
int do_deflector;
float fieldfactor;
float windfactor;
@@ -1470,7 +1470,7 @@ static int sb_detect_edge_collisionCached(float edge_v1[3], float edge_v2[3], fl
return deflected;
}
static void _scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow, int ifirst, int ilast, struct ListBase *do_effector)
static void _scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow, int ifirst, int ilast, struct EffectorContext *do_effector)
{
SoftBody *sb = ob->soft;
int a;
@@ -1547,11 +1547,11 @@ static void _scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow,
static void scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow)
{
SoftBody *sb = ob->soft;
ListBase *do_effector = NULL;
EffectorContext *do_effector = NULL;
do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights, true);
_scan_for_ext_spring_forces(scene, ob, timenow, 0, sb->totspring, do_effector);
pdEndEffectors(&do_effector);
pdEndEffectors(do_effector);
}
static void *exec_scan_for_ext_spring_forces(void *data)
@@ -1563,13 +1563,13 @@ static void *exec_scan_for_ext_spring_forces(void *data)
static void sb_sfesf_threads_run(Scene *scene, struct Object *ob, float timenow, int totsprings, int *UNUSED(ptr_to_break_func(void)))
{
ListBase *do_effector = NULL;
EffectorContext *do_effector = NULL;
ListBase threads;
SB_thread_context *sb_threads;
int i, totthread, left, dec;
int lowsprings =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */
do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
do_effector = pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
/* figure the number of threads while preventing pretty pointless threading overhead */
totthread= BKE_scene_num_threads(scene);
@@ -1614,7 +1614,7 @@ static void sb_sfesf_threads_run(Scene *scene, struct Object *ob, float timenow,
/* clean up */
MEM_freeN(sb_threads);
pdEndEffectors(&do_effector);
pdEndEffectors(do_effector);
}
@@ -1965,7 +1965,7 @@ static void sb_spring_force(Object *ob, int bpi, BodySpring *bs, float iks, floa
/* since this is definitely the most CPU consuming task here .. try to spread it */
/* core function _softbody_calc_forces_slice_in_a_thread */
/* result is int to be able to flag user break */
static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, Object *ob, float forcetime, float timenow, int ifirst, int ilast, int *UNUSED(ptr_to_break_func(void)), ListBase *do_effector, int do_deflector, float fieldfactor, float windfactor)
static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, Object *ob, float forcetime, float timenow, int ifirst, int ilast, int *UNUSED(ptr_to_break_func(void)), EffectorContext *do_effector, int do_deflector, float fieldfactor, float windfactor)
{
float iks;
int bb, do_selfcollision, do_springcollision, do_aero;
@@ -2176,7 +2176,7 @@ static void *exec_softbody_calc_forces(void *data)
return NULL;
}
static void sb_cf_threads_run(Scene *scene, Object *ob, float forcetime, float timenow, int totpoint, int *UNUSED(ptr_to_break_func(void)), struct ListBase *do_effector, int do_deflector, float fieldfactor, float windfactor)
static void sb_cf_threads_run(Scene *scene, Object *ob, float forcetime, float timenow, int totpoint, int *UNUSED(ptr_to_break_func(void)), struct EffectorContext *do_effector, int do_deflector, float fieldfactor, float windfactor)
{
ListBase threads;
SB_thread_context *sb_threads;
@@ -2238,7 +2238,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl
*/
SoftBody *sb= ob->soft; /* is supposed to be there */
/*BodyPoint *bproot;*/ /* UNUSED */
ListBase *do_effector = NULL;
EffectorContext *do_effector = NULL;
/* float gravity; */ /* UNUSED */
/* float iks; */
float fieldfactor = -1.0f, windfactor = 0.25;
@@ -2272,7 +2272,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl
if (ob->softflag & OB_SB_FACECOLL) scan_for_ext_face_forces(ob, timenow);
/* finish matrix and solve */
pdEndEffectors(&do_effector);
pdEndEffectors(do_effector);
}
@@ -2299,7 +2299,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa
BodyPoint *bp;
/* BodyPoint *bproot; */ /* UNUSED */
BodySpring *bs;
ListBase *do_effector = NULL;
EffectorContext *do_effector = NULL;
float iks, ks, kd, gravity[3] = {0.0f, 0.0f, 0.0f};
float fieldfactor = -1.0f, windfactor = 0.25f;
float tune = sb->ballstiff;
@@ -2522,7 +2522,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa
/* finally add forces caused by face collision */
if (ob->softflag & OB_SB_FACECOLL) scan_for_ext_face_forces(ob, timenow);
pdEndEffectors(&do_effector);
pdEndEffectors(do_effector);
}
}

View File

@@ -70,6 +70,9 @@
#include "BKE_animsys.h"
#include "BKE_colortools.h"
#include "BKE_scene.h"
#include "BKE_depsgraph.h"
#include "BVM_api.h"
#include "RE_shader_ext.h"
@@ -1487,7 +1490,21 @@ bool BKE_texture_dependsOnTime(const struct Tex *texture)
void BKE_texture_get_value(
const Scene *scene, Tex *texture,
float *tex_co, TexResult *texres, bool use_color_management)
const float *tex_co, TexResult *texres,
bool use_color_management)
{
BKE_texture_get_value_ex(NULL, scene, texture,
tex_co, NULL, NULL,
texres, NULL, NULL,
use_color_management);
}
void BKE_texture_get_value_ex(
struct BVMEvalGlobals *globals,
const Scene *scene, Tex *texture,
const float *tex_co, const float *tex_dx, const float *tex_dy,
TexResult *texres, TexResult *texres_dx, TexResult *texres_dy,
bool use_color_management)
{
int result_type;
bool do_color_manage = false;
@@ -1496,8 +1513,26 @@ void BKE_texture_get_value(
do_color_manage = BKE_scene_check_color_management_enabled(scene);
}
/* no node textures for now */
result_type = multitex_ext_safe(texture, tex_co, texres, NULL, do_color_manage, false);
if (texture->use_nodes && texture->nodetree) {
struct BVMFunction *fn = BVM_gen_texture_function_llvm(texture->nodetree, true);
if (fn) {
struct BVMEvalContext *context = BVM_context_create();
BVM_eval_texture_llvm(globals,
context, fn,
texres, texres_dx, texres_dy,
tex_co, tex_dx, tex_dy,
0, 0, scene->r.cfra, false);
result_type = TEX_INT | TEX_RGB | TEX_NOR;
BVM_context_free(context);
BVM_function_llvm_release(fn);
}
}
else {
struct ImagePool *image_pool = (globals ? BVM_globals_image_pool(globals) : NULL);
result_type = multitex_ext_safe(texture, (float*)tex_co, texres, image_pool, do_color_manage, false);
}
/* if the texture gave an RGB value, we assume it didn't give a valid
* intensity, since this is in the context of modifiers don't use perceptual color conversion.
@@ -1510,3 +1545,12 @@ void BKE_texture_get_value(
copy_v3_fl(&texres->tr, texres->tin);
}
}
/* -------------------- */
/* Depsgraph evaluation */
void BKE_texture_invalidate(EvaluationContext *UNUSED(eval_ctx), Tex *tex)
{
BVM_function_bvm_cache_remove(tex->nodetree);
}

View File

@@ -33,6 +33,8 @@
* \ingroup bli
*/
#include "BLI_sys_types.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -59,6 +61,21 @@ void voronoi(float x, float y, float z, float *da, float *pa, float me, int dtyp
float cellNoise(float x, float y, float z);
void cellNoiseV(float x, float y, float z, float r_ca[3]);
/* Gabor noise */
typedef struct GaborNoiseSampler {
void (*sample)(struct GaborNoiseSampler *sampler, uint64_t rng,
float *r_omega, float *r_phi);
void (*free)(struct GaborNoiseSampler *sampler);
} GaborNoiseSampler;
float BLI_gabor_noise(float noisesize, float x, float y, float z,
float impulses, float bandwidth,
struct GaborNoiseSampler *sampler);
void BLI_gabor_noise_sampler_free(struct GaborNoiseSampler *sampler);
/* sampler for band-limited isotropic noise */
struct GaborNoiseSampler *BLI_gabor_noise_sampler_isotropic(float frequency);
extern void *__BLI_noise_linker_hack__;
#ifdef __cplusplus
}
#endif

View File

@@ -75,6 +75,7 @@ float BLI_frand(void) ATTR_WARN_UNUSED_RESULT;
void BLI_frand_unit_v3(float v[3]);
/** Return a pseudo-random (hash) float from an integer value */
int BLI_hash_rand(unsigned int seed) ATTR_WARN_UNUSED_RESULT;
float BLI_hash_frand(unsigned int seed) ATTR_WARN_UNUSED_RESULT;
/** Shuffle an array randomly using the given seed.

View File

@@ -92,6 +92,7 @@ set(SRC
intern/math_vector_inline.c
intern/memory_utils.c
intern/noise.c
intern/noise_gabor.c
intern/path_util.c
intern/polyfill2d.c
intern/polyfill2d_beautify.c

View File

@@ -34,6 +34,9 @@
#include "BLI_noise.h"
/* XXX stub for preventing linker from stripping functions */
void *__BLI_noise_linker_hack__ = BLI_gabor_noise;
/* local */
static float noise3_perlin(float vec[3]);
//static float turbulence_perlin(const float point[3], float lofreq, float hifreq);

View File

@@ -0,0 +1,288 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/*
Copyright (c) 2012 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** \file blender/blenlib/intern/noise_gabor.c
* \ingroup bli
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLI_sys_types.h"
#include "BLI_noise.h"
/* ------------------------------------------------------------------------- */
/* Local RNG definition, to avoid alloc from BLI_rand */
typedef struct RNG {
uint64_t X;
} RNG;
#define MULTIPLIER 0x5DEECE66Dll
#define MASK 0x0000FFFFFFFFFFFFll
#define ADDEND 0xB
#define LOWSEED 0x330E
BLI_INLINE void rng_init(RNG *rng, unsigned int seed)
{
rng->X = (((uint64_t) seed) << 16) | LOWSEED;
}
BLI_INLINE void rng_step(RNG *rng)
{
rng->X = (MULTIPLIER * rng->X + ADDEND) & MASK;
}
BLI_INLINE int rng_get_int(RNG *rng)
{
rng_step(rng);
return (int) (rng->X >> 17);
}
BLI_INLINE unsigned int rng_get_uint(RNG *rng)
{
rng_step(rng);
return (unsigned int) (rng->X >> 17);
}
BLI_INLINE float rng_get_float(RNG *rng)
{
return (float) rng_get_int(rng) / 0x80000000;
}
/* ------------------------------------------------------------------------- */
BLI_INLINE unsigned int cell_hash(const int index[3])
{
unsigned int n = index[0] + index[1] * 1301 + index[2] * 314159;
n ^= (n << 13);
return (n * (n * n * 15731 + 789221) + 1376312589);
}
/* Poisson distribution generator according to Knuth */
BLI_INLINE unsigned int poisson_rng(RNG *rng, float lambda)
{
const int maxiter = 100000;
const float L = expf(-lambda);
float p = 1.0f;
for (unsigned int k = 0; k < maxiter; ++k) {
p *= rng_get_float(rng);
if (p <= L)
return k;
}
return 0;
}
BLI_INLINE float gabor_kernel(
const float v[3],
float K, float a,
const float omega0[3], float phi0)
{
float envelope = K * expf(-M_PI * a*a * dot_v3v3(v, v));
float harmonic = cos(2.0f*M_PI * dot_v3v3(v, omega0) + phi0);
return envelope * harmonic;
}
static float accum_cell(const int index[3], const float offset[3],
float density, float width,
GaborNoiseSampler *sampler)
{
unsigned int hash = cell_hash(index);
/* XXX Need to use multiple RNG states to generate consistent samples with changing density.
* Could also integrate poisson rng into the sample loop to avoid using multiple rngs.
*/
RNG rng1, rng2;
int num_pulses;
rng_init(&rng1, hash);
num_pulses = poisson_rng(&rng1, density);
rng_init(&rng2, hash ^ 0xdeadbeef);
float sum = 0.0f;
for (int i = 0; i < num_pulses; ++i) {
float co[3];
co[0] = rng_get_float(&rng2);
co[1] = rng_get_float(&rng2);
co[2] = rng_get_float(&rng2);
sub_v3_v3v3(co, offset, co);
float weight = 2.0f * rng_get_float(&rng2) - 1.0f;
float omega[3], phi;
sampler->sample(sampler, rng2.X, omega, &phi);
sum += gabor_kernel(co, weight, width, omega, phi);
}
return sum;
}
static const float gabor_frequency = 2.0f;
static const float gabor_cutoff = 0.05f;
static void gabor_params(float impulses, float bandwidth, float *r_density, float *r_width)
{
float exp2_bandwidth = powf(2.0f, bandwidth);
float a = gabor_frequency * ((exp2_bandwidth - 1.0) / (exp2_bandwidth + 1.0)) * sqrtf(M_PI / M_LN2);
float r = sqrtf(-logf(gabor_cutoff) / M_PI) / a;
float r2 = r * r;
float r3 = r2 * r;
float density = impulses / (4.0f/3.0f * M_PI * r3);
*r_density = density;
*r_width = a;
}
/**
* @brief BLI_gabor_noise calculates noise using a sparse Gabor convolution.
* @param size Scaling factor for the input coordinates
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @param impulses Number of expected impulses per convolution kernel area
* @return Noise value in [0,1] interval
*/
float BLI_gabor_noise(float size, float x, float y, float z,
float impulses, float bandwidth,
GaborNoiseSampler *sampler)
{
/* impulses (N) is expected number per kernel area, density (=N/pi) is number per cell */
float density, width;
gabor_params(impulses, bandwidth, &density, &width);
float isize = (size != 0.0f ? 1.0f/size : 0.0f);
float u[3];
u[0] = x * isize;
u[1] = y * isize;
u[2] = z * isize;
int idx_center[3];
float ofs_center[3];
idx_center[0] = (int)(floor(u[0]));
idx_center[1] = (int)(floor(u[1]));
idx_center[2] = (int)(floor(u[2]));
ofs_center[0] = u[0] - floorf(u[0]) - 0.5f;
ofs_center[1] = u[1] - floorf(u[1]) - 0.5f;
ofs_center[2] = u[2] - floorf(u[2]) - 0.5f;
float sum = 0.0f;
for (int iz = -1; iz < +1; ++iz) {
for (int iy = -1; iy < +1; ++iy) {
for (int ix = -1; ix < +1; ++ix) {
int idx[3];
float ofs[3];
idx[0] = idx_center[0] + ix;
idx[1] = idx_center[1] + iy;
idx[2] = idx_center[2] + iz;
ofs[0] = ofs_center[0] - (float)ix;
ofs[1] = ofs_center[1] - (float)iy;
ofs[2] = ofs_center[2] - (float)iz;
sum += accum_cell(idx, ofs, density, width, sampler);
}
}
}
return (sum + 1.0f) * 0.5f;
}
typedef struct IsotropicSampler {
GaborNoiseSampler base;
float frequency;
} IsotropicSampler;
static void isotropic_sample(GaborNoiseSampler *_sampler, uint64_t _rng,
float *r_omega, float *r_phi)
{
IsotropicSampler *sampler = (IsotropicSampler *)_sampler;
RNG rng = { _rng };
float cos_p = 2.0f * rng_get_float(&rng) - 1.0f;
float sin_p = sqrtf(1.0f - cos_p * cos_p);
float t = 2.0f*M_PI * rng_get_float(&rng);
float sin_t = sinf(t);
float cos_t = cosf(t);
r_omega[0] = cos_t * sin_p;
r_omega[1] = sin_t * sin_p;
r_omega[2] = cos_p;
mul_v3_fl(r_omega, sampler->frequency);
*r_phi = 2.0f*M_PI * rng_get_float(&rng);
}
void BLI_gabor_noise_sampler_free(GaborNoiseSampler *sampler)
{
if (sampler->free)
sampler->free(sampler);
else
MEM_freeN(sampler);
}
GaborNoiseSampler *BLI_gabor_noise_sampler_isotropic(float frequency)
{
IsotropicSampler *sampler = MEM_mallocN(sizeof(IsotropicSampler), "isotropic gabor noise sampler");
sampler->base.sample = isotropic_sample;
sampler->base.free = NULL;
sampler->frequency = frequency;
return &sampler->base;
}

View File

@@ -285,6 +285,14 @@ void BLI_frand_unit_v3(float v[3])
BLI_rng_get_float_unit_v3(&theBLI_rng, v);
}
int BLI_hash_rand(unsigned int seed)
{
RNG rng;
BLI_rng_srandom(&rng, seed);
return BLI_rng_get_int(&rng);
}
float BLI_hash_frand(unsigned int seed)
{
RNG rng;

View File

@@ -3077,12 +3077,6 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
((ImageUser *)node->storage)->ok = 1;
}
else if ( ntree->type==NTREE_TEXTURE) {
if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME)
direct_link_curvemapping(fd, node->storage);
else if (node->type==TEX_NODE_IMAGE)
((ImageUser *)node->storage)->ok = 1;
}
}
}
link_list(fd, &ntree->links);
@@ -4933,6 +4927,9 @@ static void lib_link_object(FileData *fd, Main *main)
lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
lib_link_modifiers(fd, ob);
if (ob->nodetree)
ob->nodetree = newlibadr(fd, ob->id.lib, ob->nodetree);
if (ob->rigidbody_constraint) {
ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1);
ob->rigidbody_constraint->ob2 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob2);
@@ -9269,6 +9266,9 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
modifiers_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data);
}
if (ob->nodetree)
expand_nodetree(fd, mainvar, ob->nodetree);
expand_pose(fd, mainvar, ob->pose);
expand_doit(fd, mainvar, ob->poselib);
expand_constraints(fd, mainvar, &ob->constraints);

View File

@@ -856,16 +856,9 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
/* and texture trees */
for (tx = main->tex.first; tx; tx = tx->id.next) {
bNode *node;
if (tx->nodetree) {
if (tx->nodetree->id.name[0] == '\0')
strcpy(tx->nodetree->id.name, "NTTexture Nodetree");
/* which_output 0 is now "not specified" */
for (node = tx->nodetree->nodes.first; node; node = node->next)
if (node->type == TEX_NODE_OUTPUT)
node->custom1++;
}
}

View File

@@ -1018,11 +1018,6 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree)
{
write_curvemapping(wd, node->storage);
}
else if ((ntree->type == NTREE_TEXTURE) &&
(node->type == TEX_NODE_CURVE_RGB || node->type == TEX_NODE_CURVE_TIME))
{
write_curvemapping(wd, node->storage);
}
else if ((ntree->type == NTREE_COMPOSIT) &&
(node->type == CMP_NODE_MOVIEDISTORTION))
{

View File

@@ -0,0 +1,207 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_API_H__
#define __BVM_API_H__
/** \file blender/blenvm/BVM_api.h
* \ingroup bvm
*/
#include <stdio.h>
#include "BVM_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Generic handle for functions.
* Warning: This is used for all backends! Callers must know which backend to use!
*/
struct BVMFunction;
void BVM_init(void);
void BVM_free(void);
/* ------------------------------------------------------------------------- */
struct BVMNodeGraph;
struct BVMNodeInstance;
struct BVMNodeInput;
struct BVMNodeOutput;
struct BVMTypeDesc;
struct BVMNodeInstance *BVM_nodegraph_add_node(struct BVMNodeGraph *graph, const char *type, const char *name);
void BVM_nodegraph_get_input(struct BVMNodeGraph *graph, const char *name,
struct BVMNodeInstance **node, const char **socket);
void BVM_nodegraph_get_output(struct BVMNodeGraph *graph, const char *name,
struct BVMNodeInstance **node, const char **socket);
int BVM_node_num_inputs(struct BVMNodeInstance *node);
int BVM_node_num_outputs(struct BVMNodeInstance *node);
struct BVMNodeInput *BVM_node_get_input(struct BVMNodeInstance *node, const char *name);
struct BVMNodeInput *BVM_node_get_input_n(struct BVMNodeInstance *node, int index);
struct BVMNodeOutput *BVM_node_get_output(struct BVMNodeInstance *node, const char *name);
struct BVMNodeOutput *BVM_node_get_output_n(struct BVMNodeInstance *node, int index);
bool BVM_node_set_input_link(struct BVMNodeInstance *node, struct BVMNodeInput *input,
struct BVMNodeInstance *from_node, struct BVMNodeOutput *from_output);
void BVM_node_set_input_value_float(struct BVMNodeInstance *node, struct BVMNodeInput *input, float value);
void BVM_node_set_input_value_float3(struct BVMNodeInstance *node, struct BVMNodeInput *input, const float value[3]);
void BVM_node_set_input_value_float4(struct BVMNodeInstance *node, struct BVMNodeInput *input, const float value[4]);
void BVM_node_set_input_value_matrix44(struct BVMNodeInstance *node, struct BVMNodeInput *input, float value[4][4]);
void BVM_node_set_input_value_int(struct BVMNodeInstance *node, struct BVMNodeInput *input, int value);
const char *BVM_node_input_name(struct BVMNodeInput *input);
struct BVMTypeDesc *BVM_node_input_typedesc(struct BVMNodeInput *input);
BVMInputValueType BVM_node_input_value_type(struct BVMNodeInput *input);
const char *BVM_node_output_name(struct BVMNodeOutput *output);
struct BVMTypeDesc *BVM_node_output_typedesc(struct BVMNodeOutput *output);
BVMOutputValueType BVM_node_output_value_type(struct BVMNodeOutput *output);
BVMType BVM_typedesc_base_type(struct BVMTypeDesc *typedesc);
BVMBufferType BVM_typedesc_buffer_type(struct BVMTypeDesc *typedesc);
/* ------------------------------------------------------------------------- */
struct bNodeTree;
struct DepsNodeHandle;
void BVM_nodetree_compile_dependencies(struct bNodeTree *ntree, struct DepsNodeHandle *handle);
void BVM_nodetree_eval_dependencies(struct bNodeTree *ntree, struct DepsNodeHandle *handle);
/* ------------------------------------------------------------------------- */
struct BVMEvalGlobals;
struct BVMEvalContext;
struct bNodeTree;
struct ImagePool;
struct BVMEvalGlobals *BVM_globals_create(void);
void BVM_globals_free(struct BVMEvalGlobals *globals);
struct ImagePool *BVM_globals_image_pool(struct BVMEvalGlobals *globals);
void BVM_globals_add_object(struct BVMEvalGlobals *globals, int key, struct Object *ob);
void BVM_globals_add_nodetree_relations(struct BVMEvalGlobals *globals, struct bNodeTree *ntree);
int BVM_get_id_key(struct ID *id);
struct BVMEvalContext *BVM_context_create(void);
void BVM_context_free(struct BVMEvalContext *context);
typedef enum BVMDebugMode {
BVM_DEBUG_NODES,
BVM_DEBUG_NODES_UNOPTIMIZED,
BVM_DEBUG_BVM_CODE,
BVM_DEBUG_LLVM_CODE,
BVM_DEBUG_LLVM_CODE_UNOPTIMIZED,
} BVMDebugMode;
/* ------------------------------------------------------------------------- */
void BVM_function_bvm_release(struct BVMFunction *fn);
void BVM_function_bvm_cache_remove(void *key);
void BVM_function_llvm_release(struct BVMFunction *fn);
void BVM_function_llvm_cache_remove(void *key);
/* ------------------------------------------------------------------------- */
struct Object;
struct EffectedPoint;
struct BVMFunction *BVM_gen_forcefield_function_bvm(struct bNodeTree *btree, bool use_cache);
void BVM_debug_forcefield_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode);
void BVM_eval_forcefield_bvm(struct BVMEvalGlobals *globals, struct BVMEvalContext *context, struct BVMFunction *fn,
struct Object *effob, const struct EffectedPoint *point, float force[3], float impulse[3]);
/* ------------------------------------------------------------------------- */
struct Tex;
struct TexResult;
struct BVMFunction *BVM_gen_texture_function_bvm(struct bNodeTree *btree, bool use_cache);
struct BVMFunction *BVM_gen_texture_function_llvm(struct bNodeTree *btree, bool use_cache);
void BVM_debug_texture_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode);
void BVM_eval_texture_bvm(struct BVMEvalGlobals *globals, struct BVMEvalContext *context,
struct BVMFunction *fn,
struct TexResult *target,
float coord[3], float dxt[3], float dyt[3], int osatex,
short which_output, int cfra, int preview);
void BVM_eval_texture_llvm(struct BVMEvalGlobals *globals, struct BVMEvalContext *context,
struct BVMFunction *fn,
struct TexResult *value,
struct TexResult *value_dx,
struct TexResult *value_dy,
const float coord[3], const float dxt[3], const float dyt[3], int osatex,
short which_output, int cfra, int preview);
/* ------------------------------------------------------------------------- */
struct DerivedMesh;
struct Mesh;
struct BVMFunction *BVM_gen_modifier_function_bvm(struct bNodeTree *btree, bool use_cache);
struct BVMFunction *BVM_gen_modifier_function_llvm(struct bNodeTree *btree, bool use_cache);
void BVM_debug_modifier_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode);
struct DerivedMesh *BVM_eval_modifier_bvm(struct BVMEvalGlobals *globals,
struct BVMEvalContext *context,
struct BVMFunction *fn,
struct Object *ob,
struct Mesh *base_mesh);
struct DerivedMesh *BVM_eval_modifier_llvm(struct BVMEvalGlobals *globals,
struct BVMEvalContext *context,
struct BVMFunction *fn,
struct Object *ob,
struct Mesh *base_mesh);
/* ------------------------------------------------------------------------- */
struct DupliContainer;
struct BVMFunction *BVM_gen_dupli_function_bvm(struct bNodeTree *btree, bool use_cache);
void BVM_debug_dupli_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode);
void BVM_eval_dupli_bvm(struct BVMEvalGlobals *globals,
struct BVMEvalContext *context,
struct BVMFunction *fn,
struct Object *object,
struct DupliContainer *duplicont);
#ifdef __cplusplus
}
#endif
#endif /* __BVM_API_H__ */

View File

@@ -0,0 +1,72 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_TYPES_H__
#define __BVM_TYPES_H__
/** \file blender/blenvm/BVM_types.h
* \ingroup bvm
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum BVMType {
BVM_FLOAT,
BVM_FLOAT3,
BVM_FLOAT4,
BVM_INT,
BVM_MATRIX44,
BVM_STRING,
BVM_RNAPOINTER,
BVM_MESH,
BVM_DUPLIS,
} BVMType;
typedef enum BVMBufferType {
BVM_BUFFER_SINGLE,
BVM_BUFFER_ARRAY,
BVM_BUFFER_IMAGE,
} BVMBufferType;
typedef enum BVMInputValueType {
INPUT_CONSTANT,
INPUT_EXPRESSION,
INPUT_VARIABLE,
} BVMInputValueType;
typedef enum BVMOutputValueType {
OUTPUT_EXPRESSION,
OUTPUT_VARIABLE,
} BVMOutputValueType;
#ifdef __cplusplus
}
#endif
#endif /* __BVM_TYPES_H__ */

View File

@@ -0,0 +1,120 @@
# ***** 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.
#
# The Original Code is Copyright (C) 2015, Blender Foundation
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): Lukas Toenne.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
bvm
compile
llvm
intern
util
../blenkernel
../blenlib
../depsgraph
../imbuf
../makesdna
../makesrna
../render/extern/include
../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
intern/bvm_api.cc
intern/function.cc
intern/function.h
intern/function_cache.cc
intern/function_cache.h
modules/modules.h
modules/mod_defines.h
modules/mod_base.h
modules/mod_color.h
modules/mod_image.h
modules/mod_math.h
modules/mod_mesh.h
modules/mod_object.h
modules/mod_texture.h
util/util_array.h
util/util_data_ptr.h
util/util_debug.h
util/util_eval_globals.cc
util/util_eval_globals.h
util/util_hash.h
util/util_map.h
util/util_math.h
util/util_opcode.h
util/util_string.h
util/util_structs.h
util/util_thread.h
BVM_api.h
BVM_types.h
)
add_definitions(
-DBVM_MOD_NAMESPACE_BEGIN=namespace\ blenvm\ {\ namespace\ modules\ {
-DBVM_MOD_NAMESPACE_END=}}
)
TEST_UNORDERED_MAP_SUPPORT()
if(HAVE_STD_UNORDERED_MAP_HEADER)
if(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE)
add_definitions(-DBVM_STD_UNORDERED_MAP)
else()
if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
add_definitions(-DBVM_STD_UNORDERED_MAP_IN_TR1_NAMESPACE)
else()
add_definitions(-DBVM_NO_UNORDERED_MAP)
message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
endif()
endif()
else()
if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
add_definitions(-DBVM_TR1_UNORDERED_MAP)
else()
add_definitions(-DBVM_NO_UNORDERED_MAP)
message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
endif()
endif()
if(WIN32 AND MSVC)
set(RTTI_DISABLE_FLAGS "/GR- -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
elseif(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
endif()
add_subdirectory(bvm)
add_subdirectory(compile)
if(WITH_LLVM)
add_subdirectory(llvm)
add_definitions(-DWITH_LLVM)
endif()
blender_add_lib(bf_blenvm "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -0,0 +1,63 @@
# ***** 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.
#
# The Original Code is Copyright (C) 2015, Blender Foundation
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): Lukas Toenne.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
..
../compile
../intern
../modules
../util
../../blenkernel
../../blenlib
../../bmesh
../../imbuf
../../makesdna
../../makesrna
../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
bvm_codegen.cc
bvm_codegen_debug.cc
bvm_codegen.h
bvm_eval.cc
bvm_eval.h
bvm_eval_common.h
bvm_eval_curve.h
bvm_eval_image.h
bvm_eval_math.h
bvm_eval_mesh.h
bvm_eval_texture.h
bvm_function.h
bvm_function.cc
bvm_instruction_list.cc
bvm_instruction_list.h
)
blender_add_lib(bf_blenvm_bvm "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -0,0 +1,835 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file bvm_codegen.cc
* \ingroup bvm
*/
#include <cstdio>
#include <set>
#include "node_graph.h"
#include "bvm_codegen.h"
#include "bvm_eval.h"
#include "bvm_function.h"
namespace blenvm {
Value::Value(StackIndex stack_index) :
m_stack_index(stack_index),
m_constant_value(NULL)
{
}
Value::~Value()
{
if (m_constant_value)
delete m_constant_value;
}
void Value::set_constant_value(const NodeConstant *value)
{
if (m_constant_value)
delete m_constant_value;
m_constant_value = value->copy();
}
/* ------------------------------------------------------------------------- */
BVMCodeGenerator::BVMCodeGenerator()
{
m_stack_users.resize(BVM_STACK_SIZE, 0);
}
BVMCodeGenerator::~BVMCodeGenerator()
{
}
ValueHandle BVMCodeGenerator::get_handle(const Value *value)
{
return (ValueHandle)value;
}
void BVMCodeGenerator::finalize_function()
{
}
void BVMCodeGenerator::debug_function(FILE *file)
{
}
void BVMCodeGenerator::node_graph_begin(const string &name, const NodeGraph *graph, bool use_globals)
{
/* storage for function arguments */
{
size_t num_inputs = graph->inputs.size();
for (int i = 0; i < num_inputs; ++i) {
const NodeGraph::Input *input = graph->get_input(i);
const TypeSpec *typespec = input->typedesc.get_typespec();
m_input_args.push_back(create_value(typespec));
}
}
}
void BVMCodeGenerator::node_graph_end()
{
}
void BVMCodeGenerator::store_return_value(size_t output_index, const TypeSpec *UNUSED(typespec), ValueHandle handle)
{
Value *value = m_value_map.at(handle);
m_output_args[output_index] = value;
}
ValueHandle BVMCodeGenerator::map_argument(size_t input_index, const TypeSpec *UNUSED(typespec))
{
Value *arg = m_input_args[input_index];
ValueHandle handle = get_handle(arg);
bool ok = m_value_map.insert(HandleValueMap::value_type(handle, arg)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
Value *BVMCodeGenerator::create_value(const TypeSpec *typespec)
{
StackIndex stack_index = find_stack_index(typespec->size());
m_values.push_back(Value(stack_index));
Value *value = &m_values.back();
return value;
}
ValueHandle BVMCodeGenerator::alloc_node_value(const TypeSpec *typespec, const string &UNUSED(name))
{
Value *value = create_value(typespec);
ValueHandle handle = get_handle(value);
bool ok = m_value_map.insert(HandleValueMap::value_type(handle, value)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
ValueHandle BVMCodeGenerator::create_constant(const TypeSpec *UNUSED(typespec), const NodeConstant *node_value)
{
m_values.push_back(Value(BVM_STACK_INVALID));
Value *value = &m_values.back();
value->set_constant_value(node_value);
ValueHandle handle = get_handle(value);
bool ok = m_value_map.insert(HandleValueMap::value_type(handle, value)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
void BVMCodeGenerator::eval_node(const NodeType *nodetype,
ArrayRef<ValueHandle> input_args,
ArrayRef<ValueHandle> output_args)
{
}
StackIndex BVMCodeGenerator::find_stack_index(int size) const
{
int unused = 0;
for (int i = 0; i < BVM_STACK_SIZE; ++i) {
if (m_stack_users[i] == 0) {
++unused;
if (unused == size)
return i + 1 - size;
}
else
unused = 0;
}
// TODO better reporting ...
printf("ERROR: out of stack space");
return BVM_STACK_INVALID;
}
StackIndex BVMCodeGenerator::assign_stack_index(const TypeDesc &typedesc)
{
const TypeSpec *typespec = typedesc.get_typespec();
int stack_size = EvalStack::stack_size(typespec->size());
StackIndex stack_offset = find_stack_index(stack_size);
for (int i = 0; i < stack_size; ++i) {
// TODO keep track of value users
m_stack_users[stack_offset + i] += 1;
}
return stack_offset;
}
/* ========================================================================= */
BVMCompilerBase::BVMCompilerBase()
{
stack_users.resize(BVM_STACK_SIZE, 0);
}
BVMCompilerBase::~BVMCompilerBase()
{
}
static bool is_parent_block(const NodeBlock *parent, const NodeBlock *block)
{
while (block->parent()) {
block = block->parent();
if (block == parent)
return true;
}
return false;
}
void BVMCompilerBase::calc_node_dependencies(const NodeInstance *node, BlockDependencyMap &block_deps_map)
{
if (node_deps_map.find(node) != node_deps_map.end())
return;
OutputSet &node_deps = node_deps_map[node];
OutputSet &block_deps = block_deps_map.at(node->block);
for (int i = 0; i < node->num_inputs(); ++i) {
ConstInputKey input = node->input(i);
if (input.link()) {
const NodeInstance *link_node = input.link().node;
if (link_node->block == node->block
|| is_parent_block(node->block, link_node->block))
node_deps.insert(input.link());
else if (is_parent_block(link_node->block, node->block)) {
block_deps.insert(input.link());
}
if (input.value_type() == INPUT_EXPRESSION) {
if (link_node->block != node->block) {
calc_block_dependencies(link_node->block, block_deps_map);
const OutputSet &expr_deps = block_deps_map.at(link_node->block);
node_deps.insert(expr_deps.begin(), expr_deps.end());
}
}
}
}
}
void BVMCompilerBase::calc_block_dependencies(const NodeBlock *block, BlockDependencyMap &block_deps_map)
{
if (block_deps_map.find(block) != block_deps_map.end())
return;
block_deps_map[block] = OutputSet();
for (NodeSet::const_iterator it = block->nodes().begin(); it != block->nodes().end(); ++it) {
const NodeInstance *node = *it;
calc_node_dependencies(node, block_deps_map);
}
}
void BVMCompilerBase::calc_symbol_scope(const NodeGraph &graph)
{
{
BlockDependencyMap block_deps_map;
calc_block_dependencies(&graph.main_block(), block_deps_map);
}
assert(output_users.empty());
for (NodeDependencyMap::const_iterator it = node_deps_map.begin(); it != node_deps_map.end(); ++it) {
const OutputSet &deps = it->second;
for (OutputSet::const_iterator it_dep = deps.begin(); it_dep != deps.end(); ++it_dep) {
ConstOutputKey output = *it_dep;
++output_users[output];
}
}
/* retain graph outputs for the main caller */
for (NodeGraph::OutputList::const_iterator it = graph.outputs.begin(); it != graph.outputs.end(); ++it) {
const NodeGraph::Output &output = *it;
if (output.key)
++output_users[output.key];
}
}
StackIndex BVMCompilerBase::find_stack_index(int size) const
{
int unused = 0;
for (int i = 0; i < BVM_STACK_SIZE; ++i) {
if (stack_users[i] == 0) {
++unused;
if (unused == size)
return i + 1 - size;
}
else
unused = 0;
}
// TODO better reporting ...
printf("ERROR: out of stack space");
return BVM_STACK_INVALID;
}
StackIndex BVMCompilerBase::assign_stack_index(const TypeDesc &typedesc)
{
const TypeSpec *typespec = typedesc.get_typespec();
int stack_size = EvalStack::stack_size(typespec->size());
StackIndex stack_offset = find_stack_index(stack_size);
for (int i = 0; i < stack_size; ++i) {
// TODO keep track of value users
stack_users[stack_offset + i] += 1;
}
return stack_offset;
}
void BVMCompilerBase::get_local_arg_indices(const NodeInstance *node, const NodeBlock *local_block)
{
#if 0 /* XXX deprecated */
for (int i = 0; i < node->num_outputs(); ++i) {
ConstOutputKey output = node->output(i);
if (output.socket->value_type == OUTPUT_VARIABLE) {
ConstOutputKey local_output = local_block->local_arg(output.socket->name);
output_index[local_output] = output_index.at(output);
}
}
#else
UNUSED_VARS(node, local_block);
#endif
}
void BVMCompilerBase::resolve_node_block_symbols(const NodeBlock *block)
{
assert(block_info.find(block) == block_info.end());
block_info[block] = BlockInfo();
OrderedNodeSet nodes;
nodes.insert(block->nodes().begin(), block->nodes().end());
for (OrderedNodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
const NodeInstance &node = **it;
/* initialize output data stack entries */
for (int i = 0; i < node.num_outputs(); ++i) {
ConstOutputKey output = node.output(i);
StackIndex stack_index;
if (output_index.find(output) == output_index.end())
stack_index = assign_stack_index(output.socket->typedesc);
else
stack_index = output_index.at(output);
output_index[output] = stack_index;
}
/* prepare input stack entries */
for (int i = 0; i < node.num_inputs(); ++i) {
ConstInputKey input = node.input(i);
assert(input_index.find(input) == input_index.end());
if (input.value_type() == INPUT_CONSTANT) {
/* stored directly in the instructions list after creating values */
}
else if (input.value_type() == INPUT_EXPRESSION) {
ConstOutputKey link = input.link();
const NodeBlock *expr_block = link.node->block;
if (block_info.find(expr_block) == block_info.end()) {
/* copy local arguments */
get_local_arg_indices(&node, expr_block);
resolve_node_block_symbols(expr_block);
}
StackIndex return_index = output_index.at(link);
input_index[input] = return_index;
}
else {
ConstOutputKey link = input.link();
input_index[input] = output_index.at(link);
}
}
}
}
void BVMCompilerBase::resolve_symbols(const NodeGraph &graph)
{
/* recursively assign stack indices to outputs */
resolve_node_block_symbols(&graph.main_block());
}
void BVMCompilerBase::push_constant(const NodeConstant *value) const
{
const TypeSpec *typespec = value->typedesc().get_typespec();
BLI_assert(value != NULL);
switch (typespec->base_type()) {
case BVM_FLOAT: {
float f = 0.0f;
value->get(&f);
push_float(f);
break;
}
case BVM_FLOAT3: {
float3 f = float3(0.0f, 0.0f, 0.0f);
value->get(&f);
push_float3(f);
break;
}
case BVM_FLOAT4: {
float4 f = float4(0.0f, 0.0f, 0.0f, 0.0f);
value->get(&f);
push_float4(f);
break;
}
case BVM_INT: {
int i = 0;
value->get(&i);
push_int(i);
break;
}
case BVM_MATRIX44: {
matrix44 m = matrix44::identity();
value->get(&m);
push_matrix44(m);
break;
}
case BVM_STRING: {
const char *s = "";
value->get(&s);
push_string(s);
break;
}
case BVM_RNAPOINTER: {
/* RNAPOINTER type can not be stored as a constant */
break;
}
case BVM_MESH: {
/* MESH type can not be stored as a constant */
break;
}
case BVM_DUPLIS: {
/* DUPLIS type can not be stored as a constant */
break;
}
}
}
void BVMCompilerBase::codegen_value(const NodeConstant *value, StackIndex offset) const
{
const TypeSpec *typespec = value->typedesc().get_typespec();
switch (typespec->base_type()) {
case BVM_FLOAT: {
float f = 0.0f;
value->get(&f);
push_opcode(OP_VALUE_FLOAT);
push_float(f);
push_stack_index(offset);
break;
}
case BVM_FLOAT3: {
float3 f = float3(0.0f, 0.0f, 0.0f);
value->get(&f);
push_opcode(OP_VALUE_FLOAT3);
push_float3(f);
push_stack_index(offset);
break;
}
case BVM_FLOAT4: {
float4 f = float4(0.0f, 0.0f, 0.0f, 0.0f);
value->get(&f);
push_opcode(OP_VALUE_FLOAT4);
push_float4(f);
push_stack_index(offset);
break;
}
case BVM_INT: {
int i = 0;
value->get(&i);
push_opcode(OP_VALUE_INT);
push_int(i);
push_stack_index(offset);
break;
}
case BVM_MATRIX44: {
matrix44 m = matrix44::identity();
value->get(&m);
push_opcode(OP_VALUE_MATRIX44);
push_matrix44(m);
push_stack_index(offset);
break;
}
case BVM_STRING: {
const char *s = "";
value->get(&s);
push_opcode(OP_VALUE_STRING);
push_string(s);
push_stack_index(offset);
break;
}
case BVM_RNAPOINTER: {
push_opcode(OP_VALUE_RNAPOINTER);
push_stack_index(offset);
break;
}
case BVM_MESH: {
push_opcode(OP_VALUE_MESH);
push_stack_index(offset);
break;
}
case BVM_DUPLIS: {
push_opcode(OP_VALUE_DUPLIS);
push_stack_index(offset);
break;
}
}
}
static OpCode ptr_init_opcode(const TypeDesc &td)
{
const TypeSpec *typespec = td.get_typespec();
switch (typespec->base_type()) {
case BVM_FLOAT:
case BVM_FLOAT3:
case BVM_FLOAT4:
case BVM_INT:
case BVM_MATRIX44:
case BVM_STRING:
case BVM_RNAPOINTER:
return OP_NOOP;
case BVM_MESH:
return OP_INIT_MESH_PTR;
case BVM_DUPLIS:
return OP_INIT_DUPLIS_PTR;
}
return OP_NOOP;
}
static OpCode ptr_release_opcode(const TypeDesc &td)
{
const TypeSpec *typespec = td.get_typespec();
switch (typespec->base_type()) {
case BVM_FLOAT:
case BVM_FLOAT3:
case BVM_FLOAT4:
case BVM_INT:
case BVM_MATRIX44:
case BVM_STRING:
case BVM_RNAPOINTER:
return OP_NOOP;
case BVM_MESH:
return OP_RELEASE_MESH_PTR;
case BVM_DUPLIS:
return OP_RELEASE_DUPLIS_PTR;
}
return OP_NOOP;
}
int BVMCompilerBase::codegen_node_block(const NodeBlock &block)
{
int entry_point = current_address();
BlockInfo &info = block_info[&block];
info.entry_point = entry_point;
OrderedNodeSet nodes;
nodes.insert(block.nodes().begin(), block.nodes().end());
for (OrderedNodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
const NodeInstance &node = **it;
#if 0
/* store values for unconnected inputs */
for (int i = 0; i < node.num_inputs(); ++i) {
ConstInputKey input = node.input(i);
switch (input.value_type()) {
case INPUT_CONSTANT:
/* stored directly in instructions */
break;
case INPUT_EXPRESSION:
case INPUT_VARIABLE:
/* uses linked output value on the stack */
assert(input.link());
break;
}
}
#endif
/* initialize output data stack entries */
for (int i = 0; i < node.num_outputs(); ++i) {
ConstOutputKey output = node.output(i);
/* if necessary, add a user count initializer */
OpCode init_op = ptr_init_opcode(output.socket->typedesc);
if (init_op != OP_NOOP) {
int users = output_users.at(output);
if (users > 0) {
push_opcode(init_op);
push_stack_index(output_index.at(output));
push_int(users);
}
}
}
OpCode op = get_opcode_from_node_type(node.type->name());
if (op != OP_NOOP) {
/* write main opcode */
push_opcode(op);
/* write input stack offsets and constants */
for (int i = 0; i < node.num_inputs(); ++i) {
ConstInputKey input = node.input(i);
ConstOutputKey link = input.link();
switch (input.value_type()) {
case INPUT_CONSTANT:
push_constant(input.value());
break;
case INPUT_EXPRESSION:
case INPUT_VARIABLE:
/* XXX This is neither ideal nor final:
* Ultimately loops, conditionals, etc. should
* be coded explicitly in the instructions,
* but for the time being we leave it to complex
* kernel blackbox functions to call functions.
* Every kernel op simply gets a jump address in front
* of each input variable, so we don't need an extra
* qualifier per input.
*/
if (node.type->kind() == NODE_TYPE_KERNEL) {
if (link.node->block->parent() == &block) {
const NodeBlock *expr_block = input.link().node->block;
push_jump_address(block_info.at(expr_block).entry_point);
}
else
push_jump_address(BVM_JMP_INVALID);
}
push_stack_index(input_index.at(input));
break;
}
}
/* write output stack offsets */
for (int i = 0; i < node.num_outputs(); ++i) {
ConstOutputKey output = node.output(i);
push_stack_index(output_index.at(output));
}
}
/* release input data stack entries */
{
const OutputSet &node_deps = node_deps_map.at(&node);
for (OutputSet::const_iterator it_dep = node_deps.begin(); it_dep != node_deps.end(); ++it_dep) {
ConstOutputKey output = *it_dep;
OpCode release_op = ptr_release_opcode(output.socket->typedesc);
if (release_op != OP_NOOP) {
push_opcode(release_op);
push_stack_index(output_index.at(output));
}
}
}
}
push_opcode(OP_END);
return entry_point;
}
int BVMCompilerBase::codegen_graph(const NodeGraph &graph)
{
calc_symbol_scope(graph);
/* do internal blocks first */
for (NodeGraph::NodeBlockList::const_reverse_iterator it = graph.blocks.rbegin(); it != graph.blocks.rend(); ++it) {
const NodeBlock &block = *it;
codegen_node_block(block);
}
return block_info[&graph.main_block()].entry_point;
}
/* ========================================================================= */
BVMCompiler::BVMCompiler() :
fn(NULL)
{
}
BVMCompiler::~BVMCompiler()
{
}
void BVMCompiler::push_opcode(OpCode op) const
{
fn->add_instruction(op);
}
void BVMCompiler::push_stack_index(StackIndex arg) const
{
if (arg != BVM_STACK_INVALID)
fn->add_instruction(arg);
}
void BVMCompiler::push_jump_address(int address) const
{
fn->add_instruction(int_to_instruction(address));
}
void BVMCompiler::push_float(float f) const
{
fn->add_instruction(float_to_instruction(f));
}
void BVMCompiler::push_float3(float3 f) const
{
fn->add_instruction(float_to_instruction(f.x));
fn->add_instruction(float_to_instruction(f.y));
fn->add_instruction(float_to_instruction(f.z));
}
void BVMCompiler::push_float4(float4 f) const
{
fn->add_instruction(float_to_instruction(f.x));
fn->add_instruction(float_to_instruction(f.y));
fn->add_instruction(float_to_instruction(f.z));
fn->add_instruction(float_to_instruction(f.w));
}
void BVMCompiler::push_int(int i) const
{
fn->add_instruction(int_to_instruction(i));
}
void BVMCompiler::push_matrix44(matrix44 m) const
{
fn->add_instruction(float_to_instruction(m.data[0][0]));
fn->add_instruction(float_to_instruction(m.data[0][1]));
fn->add_instruction(float_to_instruction(m.data[0][2]));
fn->add_instruction(float_to_instruction(m.data[0][3]));
fn->add_instruction(float_to_instruction(m.data[1][0]));
fn->add_instruction(float_to_instruction(m.data[1][1]));
fn->add_instruction(float_to_instruction(m.data[1][2]));
fn->add_instruction(float_to_instruction(m.data[1][3]));
fn->add_instruction(float_to_instruction(m.data[2][0]));
fn->add_instruction(float_to_instruction(m.data[2][1]));
fn->add_instruction(float_to_instruction(m.data[2][2]));
fn->add_instruction(float_to_instruction(m.data[2][3]));
fn->add_instruction(float_to_instruction(m.data[3][0]));
fn->add_instruction(float_to_instruction(m.data[3][1]));
fn->add_instruction(float_to_instruction(m.data[3][2]));
fn->add_instruction(float_to_instruction(m.data[3][3]));
}
void BVMCompiler::push_string(const char *s) const
{
const char *c = s;
while (true) {
fn->add_instruction(int_to_instruction(*(const int *)c));
if (c[0]=='\0' || c[1]=='\0' || c[2]=='\0' || c[3]=='\0')
break;
c += 4;
}
}
int BVMCompiler::current_address() const
{
return fn->get_instruction_count();
}
FunctionBVM *BVMCompiler::compile_function(const NodeGraph &graph)
{
fn = new FunctionBVM();
#if 0
resolve_symbols(graph);
int entry_point = codegen_graph(graph);
fn->set_entry_point(entry_point);
/* store stack indices for inputs/outputs, to store arguments from and return results to the caller */
for (size_t i = 0; i < graph.inputs.size(); ++i) {
const NodeGraph::Input &input = graph.inputs[i];
StackIndex stack_index;
if (input.key) {
stack_index = output_index.at(input.key);
}
else {
stack_index = BVM_STACK_INVALID;
}
fn->add_argument(input.typedesc, input.name, stack_index);
}
for (size_t i = 0; i < graph.outputs.size(); ++i) {
const NodeGraph::Output &output = graph.outputs[i];
/* every output must map to a node */
assert(output.key.node);
StackIndex stack_index = output_index.at(output.key);
fn->add_return_value(output.typedesc, output.name, stack_index);
}
#endif
FunctionBVM *result = fn;
fn = NULL;
return result;
}
} /* namespace blenvm */

View File

@@ -0,0 +1,236 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_CODEGEN_H__
#define __BVM_CODEGEN_H__
/** \file bvm_codegen.h
* \ingroup bvm
*/
#include <set>
#include <vector>
#include "MEM_guardedalloc.h"
#include "compiler.h"
#include "node_graph.h"
#include "bvm_instruction_list.h"
#include "util_opcode.h"
#include "util_string.h"
namespace blenvm {
struct FunctionBVM;
struct NodeGraph;
struct NodeInstance;
struct TypeDesc;
struct Value {
Value(StackIndex m_stack_index);
~Value();
StackIndex stack_index() const { return m_stack_index; }
const NodeConstant *constant_value() const { return m_constant_value; }
void set_constant_value(const NodeConstant *value);
private:
StackIndex m_stack_index;
NodeConstant *m_constant_value;
};
struct BVMCodeGenerator : public CodeGenerator {
typedef std::vector<Value> ValueStack;
typedef std::map<ValueHandle, Value*> HandleValueMap;
typedef std::vector<int> StackUsers;
typedef std::vector<Value*> Arguments;
BVMCodeGenerator();
~BVMCodeGenerator();
void finalize_function();
void debug_function(FILE *file);
void node_graph_begin(const string &name, const NodeGraph *graph, bool use_globals);
void node_graph_end();
void store_return_value(size_t output_index, const TypeSpec *typespec, ValueHandle value);
ValueHandle map_argument(size_t input_index, const TypeSpec *typespec);
ValueHandle alloc_node_value(const TypeSpec *typespec, const string &name);
ValueHandle create_constant(const TypeSpec *typespec, const NodeConstant *node_value);
void eval_node(const NodeType *nodetype,
ArrayRef<ValueHandle> input_args,
ArrayRef<ValueHandle> output_args);
protected:
static ValueHandle get_handle(const Value *value);
StackIndex find_stack_index(int size) const;
StackIndex assign_stack_index(const TypeDesc &typedesc);
Value *create_value(const TypeSpec *typespec);
private:
ValueStack m_values;
HandleValueMap m_value_map;
StackUsers m_stack_users;
Arguments m_input_args;
Arguments m_output_args;
InstructionList m_instructions;
};
/* ========================================================================= */
typedef std::map<ConstInputKey, StackIndex> InputIndexMap;
typedef std::map<ConstOutputKey, StackIndex> OutputIndexMap;
typedef std::map<ConstOutputKey, int> OutputUsersMap;
typedef std::map<const NodeInstance *, OutputSet> NodeDependencyMap;
typedef std::map<const NodeBlock *, OutputSet> BlockDependencyMap;
typedef std::vector<int> StackUsers;
struct BVMCompilerBase {
struct BlockInfo {
int entry_point;
};
typedef std::map<const NodeBlock*, BlockInfo> BlockInfoMap;
BVMCompilerBase();
virtual ~BVMCompilerBase();
protected:
void calc_node_dependencies(const NodeInstance *node, BlockDependencyMap &block_deps_map);
void calc_block_dependencies(const NodeBlock *block, BlockDependencyMap &block_deps_map);
void calc_symbol_scope(const NodeGraph &graph);
StackIndex find_stack_index(int size) const;
StackIndex assign_stack_index(const TypeDesc &typedesc);
void get_local_arg_indices(const NodeInstance *node, const NodeBlock *local_block);
void resolve_node_block_symbols(const NodeBlock *block);
void resolve_symbols(const NodeGraph &graph);
virtual void push_opcode(OpCode op) const = 0;
virtual void push_stack_index(StackIndex arg) const = 0;
virtual void push_jump_address(int address) const = 0;
virtual void push_float(float f) const = 0;
virtual void push_float3(float3 f) const = 0;
virtual void push_float4(float4 f) const = 0;
virtual void push_int(int i) const = 0;
virtual void push_matrix44(matrix44 m) const = 0;
virtual void push_string(const char *s) const = 0;
virtual int current_address() const = 0;
void push_constant(const NodeConstant *value) const;
void codegen_value(const NodeConstant *value, StackIndex offset) const;
int codegen_node_block(const NodeBlock &block);
int codegen_graph(const NodeGraph &graph);
protected:
BlockInfoMap block_info;
NodeDependencyMap node_deps_map;
OutputUsersMap output_users;
InputIndexMap input_index;
OutputIndexMap output_index;
StackUsers stack_users;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:BVMCompiler")
};
struct BVMCompiler : public BVMCompilerBase {
BVMCompiler();
~BVMCompiler();
FunctionBVM *compile_function(const NodeGraph &graph);
protected:
void push_opcode(OpCode op) const;
void push_stack_index(StackIndex arg) const;
void push_jump_address(int address) const;
void push_float(float f) const;
void push_float3(float3 f) const;
void push_float4(float4 f) const;
void push_int(int i) const;
void push_matrix44(matrix44 m) const;
void push_string(const char *s) const;
int current_address() const;
private:
FunctionBVM *fn;
};
struct DebugGraphvizBVMCompiler : public BVMCompilerBase {
DebugGraphvizBVMCompiler();
~DebugGraphvizBVMCompiler();
void compile_function(const NodeGraph &graph, FILE *file, const string &label);
protected:
void push_opcode(OpCode op) const;
void push_stack_index(StackIndex arg) const;
void push_jump_address(int address) const;
void push_float(float m_file) const;
void push_float3(float3 m_file) const;
void push_float4(float4 m_file) const;
void push_int(int i) const;
void push_matrix44(matrix44 m) const;
void push_string(const char *s) const;
int current_address() const;
const char *get_arg_name() const;
bool is_arg_output() const;
void init_graph(const string &label);
void close_graph();
void init_node();
void close_node();
private:
FILE *m_file;
mutable int m_current_address;
mutable const NodeType *m_current_opnode;
mutable int m_current_arg;
};
} /* namespace blenvm */
#endif /* __BVM_CODEGEN_H__ */

View File

@@ -0,0 +1,298 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file bvm_codegen_debug.cc
* \ingroup bvm
*/
#include <cstdio>
#include <set>
#include "node_graph.h"
#include "bvm_codegen.h"
#include "util_debug.h"
#include "util_string.h"
namespace blenvm {
using namespace debug;
#define NL "\r\n"
static void debug_fprintf(FILE *f, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
static void debug_fprintf(FILE *f, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(f, fmt, args);
va_end(args);
}
inline void print_rows(FILE *f, int *cur, int num, const char *color, const char *fmt, ...)
{
va_list args;
for (int i = 0; i < num; ++i) {
debug_fprintf(f, "<TR>");
debug_fprintf(f, "<TD>%d</TD>", (*cur) + i);
if (i == 0) {
debug_fprintf(f, "<TD ROWSPAN=\"%d\" BGCOLOR=\"%s\">", num, color);
va_start(args, fmt);
vfprintf(f, fmt, args);
va_end(args);
debug_fprintf(f, "</TD>");
}
debug_fprintf(f, "</TR>" NL);
}
*cur += num;
}
inline void print_gap(FILE *f)
{
debug_fprintf(f, "<TR>");
debug_fprintf(f, "<TD HEIGHT=\"5\" BORDER=\"0\">");
debug_fprintf(f, "</TD>");
debug_fprintf(f, "</TR>" NL);
}
DebugGraphvizBVMCompiler::DebugGraphvizBVMCompiler() :
m_file(NULL),
m_current_address(0),
m_current_opnode(NULL),
m_current_arg(0)
{
}
DebugGraphvizBVMCompiler::~DebugGraphvizBVMCompiler()
{
}
void DebugGraphvizBVMCompiler::push_opcode(OpCode op) const
{
const char *opname = opcode_name(op);
if (m_current_address > 0)
print_gap(m_file);
print_rows(m_file, &m_current_address, 1, color_opcode, "OP %s", opname);
m_current_opnode = NodeGraph::find_node_type(opname);
m_current_arg = 0;
}
void DebugGraphvizBVMCompiler::push_stack_index(StackIndex arg) const
{
const char *load_or_store = is_arg_output() ? "store" : "load";
print_rows(m_file, &m_current_address, 1, color_stack_index, "%s %d [%s]", load_or_store, (int)arg, get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_jump_address(int address) const
{
print_rows(m_file, &m_current_address, 1, color_jump_address, "JMP %d", (int)address);
}
void DebugGraphvizBVMCompiler::push_float(float f) const
{
print_rows(m_file, &m_current_address, 1, color_value, "%f [%s]", f, get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_float3(float3 f) const
{
print_rows(m_file, &m_current_address, 3, color_value, "(%.2f, %.2f, %.2f)<BR/>[%s]", f.x, f.y, f.z, get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_float4(float4 f) const
{
print_rows(m_file, &m_current_address, 4, color_value, "(%.2f, %.2f, %.2f, %.2f)<BR/>[%s]", f.x, f.y, f.z, f.w, get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_int(int i) const
{
print_rows(m_file, &m_current_address, 1, color_value, "%d [%s]", i, get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_matrix44(matrix44 m) const
{
print_rows(m_file, &m_current_address, 16, color_value, "%.2f, %.2f, %.2f, %.2f<BR/>%.2f, %.2f, %.2f, %.2f<BR/>%.2f, %.2f, %.2f, %.2f<BR/>%.2f, %.2f, %.2f, %.2f<BR/>[%s]",
m.data[0][0], m.data[1][0], m.data[2][0], m.data[3][0],
m.data[0][1], m.data[1][1], m.data[2][1], m.data[3][1],
m.data[0][2], m.data[1][2], m.data[2][2], m.data[3][2],
m.data[0][3], m.data[1][3], m.data[2][3], m.data[3][3],
get_arg_name());
++m_current_arg;
}
void DebugGraphvizBVMCompiler::push_string(const char *s) const
{
const char *c = s;
int rows = 0;
while (true) {
rows += 1;
if (c[0]=='\0' || c[1]=='\0' || c[2]=='\0' || c[3]=='\0')
break;
c += 4;
}
print_rows(m_file, &m_current_address, rows, color_value, "%s<BR/>[%s]", s, get_arg_name());
++m_current_arg;
}
int DebugGraphvizBVMCompiler::current_address() const
{
return m_current_address;
}
const char *DebugGraphvizBVMCompiler::get_arg_name() const
{
if (m_current_opnode) {
if (m_current_arg < m_current_opnode->num_inputs())
return m_current_opnode->find_input(m_current_arg)->name.c_str();
else
return m_current_opnode->find_output(m_current_arg - m_current_opnode->num_inputs())->name.c_str();
}
return "";
}
bool DebugGraphvizBVMCompiler::is_arg_output() const
{
if (m_current_opnode) {
return m_current_arg >= m_current_opnode->num_inputs();
}
return false;
}
void DebugGraphvizBVMCompiler::init_graph(const string &label)
{
static float label_size = 20.0f;
debug_fprintf(m_file, "digraph depgraph {" NL);
debug_fprintf(m_file, "rankdir=LR;" NL);
debug_fprintf(m_file, "graph [");
debug_fprintf(m_file, "labelloc=\"t\"");
debug_fprintf(m_file, ",fontsize=%f", label_size);
debug_fprintf(m_file, ",fontname=\"%s\"", fontname);
debug_fprintf(m_file, ",label=\"%s\"", label.c_str());
// debug_fprintf(m_file, ",splines=ortho");
debug_fprintf(m_file, "];" NL);
}
void DebugGraphvizBVMCompiler::close_graph()
{
debug_fprintf(m_file, "}" NL);
}
void DebugGraphvizBVMCompiler::init_node()
{
static float label_size = 14.0f;
const char *shape = "box";
const char *style = "filled";
const char *color = "black";
const char *fillcolor = "gainsboro";
float penwidth = 1.0f;
debug_fprintf(m_file, "instructions");
debug_fprintf(m_file, "[");
debug_fprintf(m_file, "fontname=\"%s\"", fontname);
debug_fprintf(m_file, ",fontsize=\"%f\"", label_size);
debug_fprintf(m_file, ",shape=\"%s\"", shape);
debug_fprintf(m_file, ",style=\"%s\"", style);
debug_fprintf(m_file, ",color=\"%s\"", color);
debug_fprintf(m_file, ",fillcolor=\"%s\"", fillcolor);
debug_fprintf(m_file, ",penwidth=\"%f\"", penwidth);
debug_fprintf(m_file, ",label=<<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL);
}
void DebugGraphvizBVMCompiler::close_node()
{
debug_fprintf(m_file, "</TABLE>>");
debug_fprintf(m_file, "];" NL);
}
void DebugGraphvizBVMCompiler::compile_function(const NodeGraph &graph, FILE *f, const string &label)
{
m_file = f;
m_current_address = 0;
m_current_opnode = NULL;
m_current_arg = 0;
resolve_symbols(graph);
init_graph(label);
init_node();
int entry_point = codegen_graph(graph);
(void)entry_point;
close_node();
close_graph();
#if 0
/* store stack indices for inputs/outputs, to store arguments from and return results to the caller */
for (size_t i = 0; i < graph.inputs.size(); ++i) {
const NodeGraph::Input &input = graph.inputs[i];
StackIndex stack_index;
if (input.key.node) {
stack_index = main_block().output_index.at(input.key);
}
else {
stack_index = BVM_STACK_INVALID;
}
fn->add_argument(input.typedesc, input.name, stack_index);
}
for (size_t i = 0; i < graph.outputs.size(); ++i) {
const NodeGraph::Output &output = graph.outputs[i];
/* every output must map to a node */
assert(output.key.node);
StackIndex stack_index = main_block().output_index.at(output.key);
fn->add_return_value(output.typedesc, output.name, stack_index);
}
#endif
m_current_opnode = NULL;
m_current_arg = 0;
m_file = NULL;
}
} /* namespace blenvm */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_H__
#define __BVM_EVAL_H__
/** \file bvm_eval.h
* \ingroup bvm
*/
#include "MEM_guardedalloc.h"
#include "util_eval_globals.h"
namespace blenvm {
struct InstructionList;
#define BVM_STACK_SIZE 4095
struct EvalStack {
static int stack_size(size_t datasize);
int value;
};
struct EvalContext {
EvalContext();
~EvalContext();
void eval_expression(const EvalGlobals *globals, const InstructionList *instr, int entry_point, EvalStack *stack) const;
void eval_instructions(const EvalGlobals *globals, const InstructionList *instr, int entry_point, EvalStack *stack) const;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:EvalContext")
};
} /* namespace blenvm */
#endif /* __BVM_EVAL_H__ */

View File

@@ -0,0 +1,182 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_COMMON_H__
#define __BVM_EVAL_COMMON_H__
/** \file bvm_eval_common.h
* \ingroup bvm
*/
#include "bvm_eval.h"
#include "bvm_instruction_list.h"
namespace blenvm {
struct EvalContext;
struct EvalKernelData {
const EvalContext *context;
const InstructionList *function;
};
inline static float stack_load_float(const EvalStack *stack, StackIndex offset)
{
return *(float *)(&stack[offset]);
}
inline static float3 stack_load_float3(const EvalStack *stack, StackIndex offset)
{
return *(float3 *)(&stack[offset]);
}
inline static float4 stack_load_float4(const EvalStack *stack, StackIndex offset)
{
return *(float4 *)(&stack[offset]);
}
inline static int stack_load_int(const EvalStack *stack, StackIndex offset)
{
return *(int *)(&stack[offset]);
}
inline static matrix44 stack_load_matrix44(const EvalStack *stack, StackIndex offset)
{
return *(matrix44 *)(&stack[offset]);
}
/* convenience function */
inline static SpaceTransform stack_load_space_transform(const EvalStack *stack,
StackIndex offset_transform,
StackIndex offset_invtransform)
{
SpaceTransform transform;
copy_m4_m4(transform.local2target, stack_load_matrix44(stack, offset_transform).data);
copy_m4_m4(transform.target2local, stack_load_matrix44(stack, offset_invtransform).data);
return transform;
}
inline static const char *stack_load_string(const EvalStack *stack, StackIndex offset)
{
return *(const char **)(&stack[offset]);
}
inline static PointerRNA stack_load_rnapointer(const EvalStack *stack, StackIndex offset)
{
return *(PointerRNA *)(&stack[offset]);
}
inline static mesh_ptr stack_load_mesh_ptr(const EvalStack *stack, StackIndex offset)
{
return *(mesh_ptr *)(&stack[offset]);
}
inline static DerivedMesh *stack_load_mesh(const EvalStack *stack, StackIndex offset)
{
return ((mesh_ptr *)(&stack[offset]))->get();
}
inline static duplis_ptr stack_load_duplis_ptr(const EvalStack *stack, StackIndex offset)
{
return *(duplis_ptr *)(&stack[offset]);
}
inline static const DupliList *stack_load_duplis(const EvalStack *stack, StackIndex offset)
{
return ((duplis_ptr *)(&stack[offset]))->get();
}
/* ------------------------------------------------------------------------- */
inline static void stack_store_float(EvalStack *stack, StackIndex offset, float f)
{
*(float *)(&stack[offset]) = f;
}
inline static void stack_store_float3(EvalStack *stack, StackIndex offset, float3 f)
{
*(float3 *)(&stack[offset]) = f;
}
inline static void stack_store_float4(EvalStack *stack, StackIndex offset, float4 f)
{
*(float4 *)(&stack[offset]) = f;
}
inline static void stack_store_int(EvalStack *stack, StackIndex offset, int i)
{
*(int *)(&stack[offset]) = i;
}
inline static void stack_store_matrix44(EvalStack *stack, StackIndex offset, matrix44 m)
{
*(matrix44 *)(&stack[offset]) = m;
}
/* convenience function */
inline static void stack_store_space_transform(EvalStack *stack,
StackIndex offset_transform,
StackIndex offset_invtransform,
SpaceTransform transform)
{
stack_store_matrix44(stack, offset_transform, matrix44::from_data(&transform.local2target[0][0]));
stack_store_matrix44(stack, offset_invtransform, matrix44::from_data(&transform.target2local[0][0]));
}
inline static void stack_store_string(EvalStack *stack, StackIndex offset, const char *s)
{
*(const char **)(&stack[offset]) = s;
}
inline static void stack_store_rnapointer(EvalStack *stack, StackIndex offset, PointerRNA p)
{
*(PointerRNA *)(&stack[offset]) = p;
}
inline static void stack_store_mesh_ptr(EvalStack *stack, StackIndex offset, mesh_ptr p)
{
*(mesh_ptr *)(&stack[offset]) = p;
}
inline static void stack_store_mesh(EvalStack *stack, StackIndex offset, DerivedMesh *dm)
{
((mesh_ptr *)(&stack[offset]))->set(dm);
}
inline static void stack_store_duplis_ptr(EvalStack *stack, StackIndex offset, duplis_ptr p)
{
*(duplis_ptr *)(&stack[offset]) = p;
}
inline static void stack_store_duplis(EvalStack *stack, StackIndex offset, DupliList *lb)
{
((duplis_ptr *)(&stack[offset]))->set(lb);
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_COMMON_H__ */

View File

@@ -0,0 +1,102 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_CURVE_H__
#define __BVM_EVAL_CURVE_H__
/** \file bvm_eval_curve.h
* \ingroup bvm
*/
extern "C" {
#include "BLI_math.h"
#include "BKE_anim.h"
}
#include "bvm_eval_common.h"
#include "util_math.h"
namespace blenvm {
static void eval_op_curve_path(EvalStack *stack,
StackIndex offset_object,
StackIndex offset_transform,
StackIndex offset_invtransform,
StackIndex offset_param,
StackIndex offset_loc,
StackIndex offset_dir,
StackIndex offset_nor,
StackIndex offset_rot,
StackIndex offset_radius,
StackIndex offset_weight,
StackIndex offset_tilt)
{
PointerRNA ptr = stack_load_rnapointer(stack, offset_object);
float t = stack_load_float(stack, offset_param);
/* where_on_path is touchy about 0 > t > 1 */
CLAMP(t, 0.0f, 1.0f);
float3 loc(0, 0, 0), dir(0, 0, 0), nor(0, 0, 0);
matrix44 rot = matrix44::identity();
float radius=0.0f, weight=0.0f, tilt=0.0f;
if (ptr.data && RNA_struct_is_a(&RNA_Object, ptr.type)) {
Object *ob = (Object *)ptr.data;
matrix44 omat = stack_load_matrix44(stack, offset_transform);
matrix44 imat = stack_load_matrix44(stack, offset_invtransform);
/* XXX normal (curvature) is not yet defined! */
/* XXX where_on_path expects a vec[4], and uses the last
* element for storing tilt ...
*/
float vec[4], qt[4], qtm[3][3];
where_on_path(ob, t, vec, dir.data(), qt, &radius, &weight);
mul_v3_m4v3(loc.data(), omat.data, vec);
mul_mat3_m4_v3(omat.data, dir.data());
mul_mat3_m4_v3(omat.data, nor.data());
quat_to_mat3(qtm, qt);
mul_m4_m3m4(rot.data, qtm, imat.data);
mul_m4_m4m4(rot.data, omat.data, rot.data);
tilt = vec[3];
}
stack_store_float3(stack, offset_loc, loc);
stack_store_float3(stack, offset_dir, dir);
stack_store_float3(stack, offset_nor, nor);
stack_store_matrix44(stack, offset_rot, rot);
stack_store_float(stack, offset_radius, radius);
stack_store_float(stack, offset_weight, weight);
stack_store_float(stack, offset_tilt, tilt);
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_CURVE_H__ */

View File

@@ -0,0 +1,104 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_IMAGE_H__
#define __BVM_EVAL_IMAGE_H__
/** \file bvm_eval_image.h
* \ingroup bvm
*/
extern "C" {
#include "BLI_math_color.h"
#include "DNA_image_types.h"
#include "BKE_image.h"
#include "BKE_scene.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_colormanagement.h"
}
#include "bvm_eval_common.h"
#include "util_math.h"
namespace blenvm {
/* nearest sampling mode */
inline static void imbuf_sample_nearest(struct ImBuf *ibuf, const float3 &uv, float4 &color)
{
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
int x1 = (int)uv.x;
int y1 = (int)uv.y;
/* sample area entirely outside image? */
if (x1 < 0 || x1 > ibuf->x - 1 || y1 < 0 || y1 > ibuf->y - 1) {
color.x = color.y = color.z = color.w = 0.0f;
return;
}
if (ibuf->rect_float) {
const float *data = ibuf->rect_float + ((size_t)ibuf->x * y1 + x1) * 4;
copy_v4_v4(color.data(), data);
}
else {
const unsigned char *data = (unsigned char *)ibuf->rect + ((size_t)ibuf->x * y1 + x1) * 4;
rgba_uchar_to_float(color.data(), data);
}
}
static void eval_op_image_sample(const EvalGlobals *globals,
EvalStack *stack,
StackIndex offset_image,
StackIndex offset_uv,
StackIndex offset_color)
{
float4 color(0.0f, 0.0f, 0.0f, 0.0f);
int ima_key = stack_load_int(stack, offset_image);
/* TODO just a dummy ImageUser for now */
ImageUser iuser = {0};
iuser.ok = true;
ImBuf *ibuf = globals->lookup_imbuf(ima_key, &iuser);
if (ibuf) {
float3 uv = stack_load_float3(stack, offset_uv);
imbuf_sample_nearest(ibuf, uv, color);
}
stack_store_float4(stack, offset_color, color);
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_IMAGE_H__ */

View File

@@ -0,0 +1,475 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_MATH_H__
#define __BVM_EVAL_MATH_H__
/** \file bvm_eval_math.h
* \ingroup bvm
*/
extern "C" {
#include "BLI_math.h"
#include "BLI_rand.h"
}
#include "bvm_eval_common.h"
#include "util_hash.h"
#include "util_math.h"
namespace blenvm {
static void eval_op_matrix44_to_loc(EvalStack *stack, StackIndex offset_mat, StackIndex offset_loc)
{
matrix44 m = stack_load_matrix44(stack, offset_mat);
float3 loc;
copy_v3_v3(loc.data(), m.data[3]);
stack_store_float3(stack, offset_loc, loc);
}
static void eval_op_matrix44_to_rot(EvalStack *stack, StackIndex offset_mat, StackIndex offset_rot)
{
matrix44 m = stack_load_matrix44(stack, offset_mat);
matrix44 rot;
normalize_v3_v3(rot.data[0], m.data[0]);
normalize_v3_v3(rot.data[1], m.data[1]);
normalize_v3_v3(rot.data[2], m.data[2]);
zero_v3(rot.data[3]);
rot.data[0][3] = 0.0;
rot.data[1][3] = 0.0;
rot.data[2][3] = 0.0;
rot.data[3][3] = 1.0;
stack_store_matrix44(stack, offset_rot, rot);
}
static void eval_op_matrix44_to_euler(EvalStack *stack, int order, StackIndex offset_mat, StackIndex offset_euler)
{
matrix44 m = stack_load_matrix44(stack, offset_mat);
float3 euler;
mat4_to_eulO(euler.data(), (short)order, m.data);
stack_store_float3(stack, offset_euler, euler);
}
static void eval_op_matrix44_to_axisangle(EvalStack *stack, StackIndex offset_mat, StackIndex offset_axis, StackIndex offset_angle)
{
matrix44 m = stack_load_matrix44(stack, offset_mat);
float3 axis;
float angle;
mat4_to_axis_angle(axis.data(), &angle, m.data);
stack_store_float3(stack, offset_axis, axis);
stack_store_float(stack, offset_angle, angle);
}
static void eval_op_matrix44_to_scale(EvalStack *stack, StackIndex offset_mat, StackIndex offset_scale)
{
matrix44 m = stack_load_matrix44(stack, offset_mat);
float3 scale;
mat4_to_size(scale.data(), m.data);
stack_store_float3(stack, offset_scale, scale);
}
static void eval_op_loc_to_matrix44(EvalStack *stack, StackIndex offset_loc, StackIndex offset_mat)
{
float3 loc = stack_load_float3(stack, offset_loc);
matrix44 m = matrix44::identity();
copy_v3_v3(m.data[3], loc.data());
stack_store_matrix44(stack, offset_mat, m);
}
static void eval_op_euler_to_matrix44(EvalStack *stack, int order, StackIndex offset_euler, StackIndex offset_mat)
{
float3 euler = stack_load_float3(stack, offset_euler);
matrix44 m = matrix44::identity();
eulO_to_mat4(m.data, euler.data(), (short)order);
stack_store_matrix44(stack, offset_mat, m);
}
static void eval_op_axisangle_to_matrix44(EvalStack *stack, StackIndex offset_axis, StackIndex offset_angle, StackIndex offset_mat)
{
float3 axis = stack_load_float3(stack, offset_axis);
float angle = stack_load_float(stack, offset_angle);
matrix44 m = matrix44::identity();
axis_angle_to_mat4(m.data, axis.data(), angle);
stack_store_matrix44(stack, offset_mat, m);
}
static void eval_op_scale_to_matrix44(EvalStack *stack, StackIndex offset_scale, StackIndex offset_mat)
{
float3 scale = stack_load_float3(stack, offset_scale);
matrix44 m = matrix44::identity();
size_to_mat4(m.data, scale.data());
stack_store_matrix44(stack, offset_mat, m);
}
static void eval_op_add_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, a + b);
}
static void eval_op_sub_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, a - b);
}
static void eval_op_mul_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, a * b);
}
static void eval_op_div_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, div_safe(a, b));
}
static void eval_op_sine(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, sinf(f));
}
static void eval_op_cosine(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, cosf(f));
}
static void eval_op_tangent(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, tanf(f));
}
static void eval_op_arcsine(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, asinf(f));
}
static void eval_op_arccosine(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, acosf(f));
}
static void eval_op_arctangent(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, atanf(f));
}
static void eval_op_power(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, (a >= 0.0f)? powf(a, b): 0.0f);
}
static void eval_op_logarithm(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, (a >= 0.0f && b >= 0.0f)? logf(a) / logf(b): 0.0f);
}
static void eval_op_minimum(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, min_ff(a, b));
}
static void eval_op_maximum(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, max_ff(a, b));
}
static void eval_op_round(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, floorf(f + 0.5f));
}
static void eval_op_less_than(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, (a < b) ? 1.0f : 0.0f);
}
static void eval_op_greater_than(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, (a > b) ? 1.0f : 0.0f);
}
static void eval_op_modulo(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float(stack, offset_r, (b != 0.0f) ? fmodf(a, b) : 0.0f);
}
static void eval_op_absolute(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, fabsf(f));
}
static void eval_op_clamp(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
float f = stack_load_float(stack, offset);
stack_store_float(stack, offset_r, CLAMPIS(f, 0.0f, 1.0f));
}
static void eval_op_sqrt_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_r)
{
float a = stack_load_float(stack, offset_a);
stack_store_float(stack, offset_r, sqrt_safe(a));
}
static void eval_op_add_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(a.x + b.x, a.y + b.y, a.z + b.z));
}
static void eval_op_sub_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(a.x - b.x, a.y - b.y, a.z - b.z));
}
static void eval_op_mul_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(a.x * b.x, a.y * b.y, a.z * b.z));
}
static void eval_op_div_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(div_safe(a.x, b.x), div_safe(a.y, b.y), div_safe(a.z, b.z)));
}
static void eval_op_mul_float3_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float3(stack, offset_r, float3(a.x * b, a.y * b, a.z * b));
}
static void eval_op_div_float3_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float b = stack_load_float(stack, offset_b);
stack_store_float3(stack, offset_r, float3(div_safe(a.x, b), div_safe(a.y, b), div_safe(a.z, b)));
}
static void eval_op_average_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(0.5f*(a.x+b.x), 0.5f*(a.y+b.y), 0.5f*(a.z+b.z)));
}
static void eval_op_dot_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float(stack, offset_r, a.x * b.x + a.y * b.y + a.z * b.z);
}
static void eval_op_cross_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
float3 a = stack_load_float3(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
stack_store_float3(stack, offset_r, float3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x));
}
static void eval_op_normalize_float3(EvalStack *stack, StackIndex offset, StackIndex offset_vec, StackIndex offset_val)
{
float3 v = stack_load_float3(stack, offset);
float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
float f = l > 0.0f ? 1.0f/l : 0.0f;
float3 vec(v.x * f, v.y * f, v.z * f);
stack_store_float3(stack, offset_vec, vec);
stack_store_float(stack, offset_val, l);
}
static void eval_op_length_float3(EvalStack *stack, StackIndex offset, StackIndex offset_len)
{
float3 v = stack_load_float3(stack, offset);
float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
stack_store_float(stack, offset_len, l);
}
static void eval_op_add_matrix44(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
matrix44 b = stack_load_matrix44(stack, offset_b);
matrix44 r;
add_m4_m4m4(r.data, a.data, b.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_sub_matrix44(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
matrix44 b = stack_load_matrix44(stack, offset_b);
matrix44 r;
sub_m4_m4m4(r.data, a.data, b.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_mul_matrix44(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
matrix44 b = stack_load_matrix44(stack, offset_b);
matrix44 r;
mul_m4_m4m4(r.data, a.data, b.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_mul_matrix44_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
float b = stack_load_float(stack, offset_b);
matrix44 r;
copy_m4_m4(r.data, a.data);
mul_m4_fl(r.data, b);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_div_matrix44_float(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
float b = stack_load_float(stack, offset_b);
matrix44 r;
copy_m4_m4(r.data, a.data);
mul_m4_fl(r.data, div_safe(1.0, b));
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_negate_matrix44(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
matrix44 m = stack_load_matrix44(stack, offset);
matrix44 r;
copy_m4_m4(r.data, m.data);
negate_m4(r.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_transpose_matrix44(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
matrix44 m = stack_load_matrix44(stack, offset);
matrix44 r;
transpose_m4_m4(r.data, m.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_invert_matrix44(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
matrix44 m = stack_load_matrix44(stack, offset);
matrix44 r;
invert_m4_m4_safe(r.data, m.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_adjoint_matrix44(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
matrix44 m = stack_load_matrix44(stack, offset);
matrix44 r;
adjoint_m4_m4(r.data, m.data);
stack_store_matrix44(stack, offset_r, r);
}
static void eval_op_determinant_matrix44(EvalStack *stack, StackIndex offset, StackIndex offset_r)
{
matrix44 m = stack_load_matrix44(stack, offset);
float d = determinant_m4(m.data);
stack_store_float(stack, offset_r, d);
}
static void eval_op_mul_matrix44_float3(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
float3 b = stack_load_float3(stack, offset_b);
float3 r;
mul_v3_m4v3(r.data(), a.data, b.data());
stack_store_float3(stack, offset_r, r);
}
static void eval_op_mul_matrix44_float4(EvalStack *stack, StackIndex offset_a, StackIndex offset_b, StackIndex offset_r)
{
matrix44 a = stack_load_matrix44(stack, offset_a);
float4 b = stack_load_float4(stack, offset_b);
float4 r;
mul_v4_m4v4(r.data(), a.data, b.data());
stack_store_float4(stack, offset_r, r);
}
static void eval_op_int_to_random(EvalStack *stack, uint64_t seed, StackIndex offset, StackIndex offset_irandom, StackIndex offset_frandom)
{
union { uint32_t u; int x; } c;
c.x = stack_load_int(stack, offset);
uint32_t r = BLI_hash_rand(hash_combine(c.u, seed));
stack_store_int(stack, offset_irandom, (int)r);
stack_store_float(stack, offset_frandom, (float)r / (0xFFFFFFFF));
}
static void eval_op_float_to_random(EvalStack *stack, uint64_t seed, StackIndex offset, StackIndex offset_irandom, StackIndex offset_frandom)
{
union { uint32_t u; float x; } c;
c.x = stack_load_float(stack, offset);
uint32_t r = BLI_hash_rand(hash_combine(c.u, seed));
stack_store_int(stack, offset_irandom, (int)r);
stack_store_float(stack, offset_frandom, (float)r / (0xFFFFFFFF));
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_MATH_H__ */

View File

@@ -0,0 +1,544 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_MESH_H__
#define __BVM_EVAL_MESH_H__
/** \file bvm_eval_mesh.h
* \ingroup bvm
*/
extern "C" {
#include "BLI_math.h"
#include "DNA_object_types.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "tools/bmesh_intersect.h"
}
#include "bvm_eval_common.h"
namespace blenvm {
static void eval_op_mesh_load(EvalStack *stack, StackIndex offset_base_mesh, StackIndex offset_mesh)
{
PointerRNA ptr = stack_load_rnapointer(stack, offset_base_mesh);
DerivedMesh *dm;
if (ptr.data && RNA_struct_is_a(&RNA_Mesh, ptr.type)) {
dm = CDDM_from_mesh((Mesh *)ptr.data);
}
else {
dm = CDDM_new(0, 0, 0, 0, 0);
}
stack_store_mesh(stack, offset_mesh, dm);
}
static void eval_op_object_final_mesh(EvalStack *stack,
StackIndex offset_object,
StackIndex offset_mesh)
{
PointerRNA ptr = stack_load_rnapointer(stack, offset_object);
DerivedMesh *result = NULL;
if (ptr.data && RNA_struct_is_a(&RNA_Object, ptr.type)) {
Object *ob = (Object *)ptr.data;
result = ob->derivedFinal;
}
if (!result)
result = CDDM_new(0, 0, 0, 0, 0);
stack_store_mesh(stack, offset_mesh, result);
}
static void dm_insert(
DerivedMesh *result, DerivedMesh *dm,
int ofs_verts, int ofs_edges, int ofs_loops, int ofs_polys)
{
int *index_orig;
int i;
MVert *mv;
MEdge *me;
MLoop *ml;
MPoly *mp;
/* needed for subsurf so arrays are allocated */
dm->getVertArray(dm);
dm->getEdgeArray(dm);
dm->getLoopArray(dm);
dm->getPolyArray(dm);
int cap_nverts = dm->getNumVerts(dm);
int cap_nedges = dm->getNumEdges(dm);
int cap_nloops = dm->getNumLoops(dm);
int cap_npolys = dm->getNumPolys(dm);
DM_copy_vert_data(dm, result, 0, ofs_verts, cap_nverts);
DM_copy_edge_data(dm, result, 0, ofs_edges, cap_nedges);
DM_copy_loop_data(dm, result, 0, ofs_loops, cap_nloops);
DM_copy_poly_data(dm, result, 0, ofs_polys, cap_npolys);
mv = CDDM_get_verts(result) + ofs_verts;
for (i = 0; i < cap_nverts; i++, mv++) {
/* Reset MVert flags for caps */
mv->flag = mv->bweight = 0;
}
/* adjust cap edge vertex indices */
me = CDDM_get_edges(result) + ofs_edges;
for (i = 0; i < cap_nedges; i++, me++) {
me->v1 += ofs_verts;
me->v2 += ofs_verts;
}
/* adjust cap poly loopstart indices */
mp = CDDM_get_polys(result) + ofs_polys;
for (i = 0; i < cap_npolys; i++, mp++) {
mp->loopstart += ofs_loops;
}
/* adjust cap loop vertex and edge indices */
ml = CDDM_get_loops(result) + ofs_loops;
for (i = 0; i < cap_nloops; i++, ml++) {
ml->v += ofs_verts;
ml->e += ofs_edges;
}
/* set origindex */
index_orig = (int *)result->getVertDataArray(result, CD_ORIGINDEX);
if (index_orig) {
copy_vn_i(index_orig + ofs_verts, cap_nverts, ORIGINDEX_NONE);
}
index_orig = (int *)result->getEdgeDataArray(result, CD_ORIGINDEX);
if (index_orig) {
copy_vn_i(index_orig + ofs_edges, cap_nedges, ORIGINDEX_NONE);
}
index_orig = (int *)result->getPolyDataArray(result, CD_ORIGINDEX);
if (index_orig) {
copy_vn_i(index_orig + ofs_polys, cap_npolys, ORIGINDEX_NONE);
}
index_orig = (int *)result->getLoopDataArray(result, CD_ORIGINDEX);
if (index_orig) {
copy_vn_i(index_orig + ofs_loops, cap_nloops, ORIGINDEX_NONE);
}
}
static void eval_op_mesh_combine(const EvalKernelData */*kernel_data*/, EvalStack *stack,
StackIndex offset_mesh_a, StackIndex offset_mesh_b, StackIndex offset_mesh_out)
{
DerivedMesh *dm_a = stack_load_mesh(stack, offset_mesh_a);
DerivedMesh *dm_b = stack_load_mesh(stack, offset_mesh_b);
int numVertsA = dm_a->getNumVerts(dm_a);
int numEdgesA = dm_a->getNumEdges(dm_a);
int numTessFacesA = dm_a->getNumTessFaces(dm_a);
int numLoopsA = dm_a->getNumLoops(dm_a);
int numPolysA = dm_a->getNumPolys(dm_a);
int numVertsB = dm_b->getNumVerts(dm_b);
int numEdgesB = dm_b->getNumEdges(dm_b);
int numTessFacesB = dm_b->getNumTessFaces(dm_b);
int numLoopsB = dm_b->getNumLoops(dm_b);
int numPolysB = dm_b->getNumPolys(dm_b);
DerivedMesh *result = CDDM_new(numVertsA + numVertsB,
numEdgesA + numEdgesB,
numTessFacesA + numTessFacesB,
numLoopsA + numLoopsB,
numPolysA + numPolysB);
dm_insert(result, dm_a, 0, 0, 0, 0);
dm_insert(result, dm_b, numVertsA, numEdgesA, numLoopsA, numPolysA);
stack_store_mesh(stack, offset_mesh_out, result);
}
#if 0
static DerivedMesh *do_array(const EvalGlobals *globals, const EvalKernelData *kernel_data, EvalStack *stack,
DerivedMesh *dm, int count,
int fn_transform, StackIndex offset_transform, StackIndex offset_iteration)
{
const bool use_recalc_normals = (dm->dirty & DM_DIRTY_NORMALS);
int chunk_nverts = dm->getNumVerts(dm);
int chunk_nedges = dm->getNumEdges(dm);
int chunk_nloops = dm->getNumLoops(dm);
int chunk_npolys = dm->getNumPolys(dm);
/* The number of verts, edges, loops, polys, before eventually merging doubles */
int result_nverts = chunk_nverts * count;
int result_nedges = chunk_nedges * count;
int result_nloops = chunk_nloops * count;
int result_npolys = chunk_npolys * count;
/* Initialize a result dm */
MVert *orig_dm_verts = dm->getVertArray(dm);
DerivedMesh *result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys);
MVert *result_dm_verts = CDDM_get_verts(result);
/* copy customdata to original geometry */
DM_copy_vert_data(dm, result, 0, 0, chunk_nverts);
DM_copy_edge_data(dm, result, 0, 0, chunk_nedges);
DM_copy_loop_data(dm, result, 0, 0, chunk_nloops);
DM_copy_poly_data(dm, result, 0, 0, chunk_npolys);
#if 0 /* XXX is this needed? comment is unintelligible */
/* subsurf for eg wont have mesh data in the
* now add mvert/medge/mface layers */
if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) {
dm->copyVertArray(dm, result_dm_verts);
}
if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) {
dm->copyEdgeArray(dm, CDDM_get_edges(result));
}
if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) {
dm->copyLoopArray(dm, CDDM_get_loops(result));
dm->copyPolyArray(dm, CDDM_get_polys(result));
}
#endif
for (int c = 0; c < count; c++) {
/* copy customdata to new geometry */
DM_copy_vert_data(result, result, 0, c * chunk_nverts, chunk_nverts);
DM_copy_edge_data(result, result, 0, c * chunk_nedges, chunk_nedges);
DM_copy_loop_data(result, result, 0, c * chunk_nloops, chunk_nloops);
DM_copy_poly_data(result, result, 0, c * chunk_npolys, chunk_npolys);
/* calculate transform for the copy */
stack_store_int(stack, offset_iteration, c);
kernel_data->context->eval_expression(globals, kernel_data->function, fn_transform, stack);
matrix44 tfm = stack_load_matrix44(stack, offset_transform);
/* apply offset to all new verts */
MVert *mv_orig = orig_dm_verts;
MVert *mv = result_dm_verts + c * chunk_nverts;
for (int i = 0; i < chunk_nverts; i++, mv++, mv_orig++) {
mul_v3_m4v3(mv->co, tfm.data, mv_orig->co);
/* We have to correct normals too, if we do not tag them as dirty! */
if (!use_recalc_normals) {
float no[3];
normal_short_to_float_v3(no, mv->no);
mul_mat3_m4_v3(tfm.data, no);
normalize_v3(no);
normal_float_to_short_v3(mv->no, no);
}
}
/* adjust edge vertex indices */
MEdge *me = CDDM_get_edges(result) + c * chunk_nedges;
for (int i = 0; i < chunk_nedges; i++, me++) {
me->v1 += c * chunk_nverts;
me->v2 += c * chunk_nverts;
}
MPoly *mp = CDDM_get_polys(result) + c * chunk_npolys;
for (int i = 0; i < chunk_npolys; i++, mp++) {
mp->loopstart += c * chunk_nloops;
}
/* adjust loop vertex and edge indices */
MLoop *ml = CDDM_get_loops(result) + c * chunk_nloops;
for (int i = 0; i < chunk_nloops; i++, ml++) {
ml->v += c * chunk_nverts;
ml->e += c * chunk_nedges;
}
}
/* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new dm!
* TODO: we may need to set other dirty flags as well?
*/
if (use_recalc_normals) {
result->dirty = (DMDirtyFlag)(result->dirty | (int)DM_DIRTY_NORMALS);
}
return result;
}
static void eval_op_mesh_array(const EvalGlobals *globals, const EvalKernelData *kernel_data, EvalStack *stack,
StackIndex offset_mesh_in, StackIndex offset_mesh_out, StackIndex offset_count,
int fn_transform, StackIndex offset_transform, StackIndex offset_iteration)
{
DerivedMesh *dm = stack_load_mesh(stack, offset_mesh_in);
int count = stack_load_int(stack, offset_count);
DerivedMesh *result = (count > 0) ?
do_array(globals, kernel_data, stack, dm, count, fn_transform, offset_transform, offset_iteration) :
CDDM_new(0, 0, 0, 0, 0);
stack_store_mesh(stack, offset_mesh_out, result);
}
static DerivedMesh *do_displace(const EvalGlobals *globals, const EvalKernelData *kernel_data, EvalStack *stack,
DerivedMesh *dm, int fn_vector, StackIndex offset_vector,
StackIndex offset_index)
{
DerivedMesh *result = CDDM_copy(dm);
MVert *orig_mv, *orig_mverts = dm->getVertArray(dm);
MVert *mv, *mverts = result->getVertArray(result);
int i, numverts = result->getNumVerts(result);
for (i = 0, mv = mverts, orig_mv = orig_mverts; i < numverts; ++i, ++mv, ++orig_mv) {
stack_store_int(stack, offset_index, i);
kernel_data->context->eval_expression(globals, kernel_data->function, fn_vector, stack);
float3 dco = stack_load_float3(stack, offset_vector);
add_v3_v3v3(mv->co, orig_mv->co, dco.data());
}
result->dirty = (DMDirtyFlag)(result->dirty | (int)DM_DIRTY_NORMALS);
return result;
}
static void eval_op_mesh_displace(const EvalGlobals *globals, const EvalKernelData *kernel_data, EvalStack *stack,
StackIndex offset_mesh_in, StackIndex offset_mesh_out,
int adr_vector, StackIndex offset_vector,
StackIndex offset_index)
{
DerivedMesh *dm = stack_load_mesh(stack, offset_mesh_in);
DerivedMesh *result = do_displace(globals, kernel_data, stack,
dm, adr_vector, offset_vector,
offset_index);
stack_store_mesh(stack, offset_mesh_out, result);
}
#if 0
static DerivedMesh *boolean_get_quick_derivedMesh(DerivedMesh *derivedData, DerivedMesh *dm, int operation)
{
DerivedMesh *result = NULL;
if (derivedData->getNumPolys(derivedData) == 0 || dm->getNumPolys(dm) == 0) {
switch (operation) {
case eBooleanModifierOp_Intersect:
result = CDDM_new(0, 0, 0, 0, 0);
break;
case eBooleanModifierOp_Union:
if (derivedData->getNumPolys(derivedData)) result = derivedData;
else result = CDDM_copy(dm);
break;
case eBooleanModifierOp_Difference:
result = derivedData;
break;
}
}
return result;
}
#endif
/* has no meaning for faces, do this so we can tell which face is which */
#define BM_FACE_TAG BM_ELEM_DRAW
/**
* Compare selected/unselected.
*/
static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
{
return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
}
static DerivedMesh *do_boolean(DerivedMesh *dm, DerivedMesh *dm_other, matrix44 &omat, matrix44 &imat,
bool separate, bool dissolve, bool connect_regions,
int boolean_mode, float threshold)
{
DerivedMesh *result = NULL;
BMesh *bm;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_DM(dm, dm_other);
// TIMEIT_START(boolean_bmesh);
bm = BM_mesh_create(&allocsize);
DM_to_bmesh_ex(dm_other, bm, true);
DM_to_bmesh_ex(dm, bm, true);
if (1) {
/* create tessface & intersect */
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
int tottri;
BMLoop *(*looptris)[3];
looptris = (BMLoop *(*)[3])MEM_mallocN(sizeof(*looptris) * looptris_tot, __func__);
BM_mesh_calc_tessellation(bm, looptris, &tottri);
/* postpone this until after tessellating
* so we can use the original normals before the vertex are moved */
{
BMIter iter;
int i;
const int i_verts_end = dm_other->getNumVerts(dm_other);
const int i_faces_end = dm_other->getNumPolys(dm_other);
BMVert *eve;
i = 0;
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
mul_m4_v3(omat.data, eve->co);
if (++i == i_verts_end) {
break;
}
}
/* we need face normals because of 'BM_face_split_edgenet'
* we could calculate on the fly too (before calling split). */
BMFace *efa;
i = 0;
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
mul_transposed_mat3_m4_v3(imat.data, efa->no);
normalize_v3(efa->no);
BM_elem_flag_enable(efa, BM_FACE_TAG); /* temp tag to test which side split faces are from */
if (++i == i_faces_end) {
break;
}
}
}
/* not needed, but normals for 'dm' will be invalid,
* currently this is ok for 'BM_mesh_intersect' */
// BM_mesh_normals_update(bm);
BM_mesh_intersect(
bm,
looptris, tottri,
bm_face_isect_pair, NULL,
false,
separate,
dissolve,
connect_regions,
boolean_mode,
threshold);
MEM_freeN(looptris);
}
result = CDDM_from_bmesh(bm, true);
BM_mesh_free(bm);
result->dirty = (DMDirtyFlag)((int)result->dirty | DM_DIRTY_NORMALS);
// TIMEIT_END(boolean_bmesh);
return result;
}
#undef BM_FACE_TAG
static void eval_op_mesh_boolean(const EvalGlobals *UNUSED(globals),
const EvalKernelData *UNUSED(kernel_data),
EvalStack *stack,
StackIndex offset_mesh_in,
StackIndex offset_object,
StackIndex offset_transform,
StackIndex offset_invtransform,
StackIndex offset_operation,
StackIndex offset_separate,
StackIndex offset_dissolve,
StackIndex offset_connect_regions,
StackIndex offset_threshold,
StackIndex offset_mesh_out)
{
PointerRNA ptr = stack_load_rnapointer(stack, offset_object);
DerivedMesh *dm = stack_load_mesh(stack, offset_mesh_in);
DerivedMesh *result = NULL;
if (ptr.data && RNA_struct_is_a(&RNA_Object, ptr.type)) {
Object *ob = (Object *)ptr.data;
DerivedMesh *dm_other = ob->derivedFinal;
if (dm_other && dm_other->getNumPolys(dm_other) > 0) {
matrix44 omat = stack_load_matrix44(stack, offset_transform);
matrix44 imat = stack_load_matrix44(stack, offset_invtransform);
int operation = stack_load_int(stack, offset_operation);
bool separate = stack_load_int(stack, offset_separate);
bool dissolve = stack_load_int(stack, offset_dissolve);
bool connect_regions = stack_load_int(stack, offset_connect_regions);
float threshold = stack_load_float(stack, offset_threshold);
result = do_boolean(dm, dm_other, omat, imat, separate, dissolve, connect_regions, operation, threshold);
}
}
if (!result)
result = CDDM_new(0, 0, 0, 0, 0);
stack_store_mesh(stack, offset_mesh_out, result);
}
#endif
static void eval_op_mesh_closest_point(EvalStack *stack,
StackIndex offset_mesh,
StackIndex offset_transform,
StackIndex offset_invtransform,
StackIndex offset_vector,
StackIndex offset_position,
StackIndex offset_normal,
StackIndex offset_tangent)
{
DerivedMesh *dm = stack_load_mesh(stack, offset_mesh);
SpaceTransform transform = stack_load_space_transform(stack, offset_transform, offset_invtransform);
float3 vec;
vec = stack_load_float3(stack, offset_vector);
BLI_space_transform_invert(&transform, &vec.x);
float3 pos(0.0f, 0.0f, 0.0f), nor(0.0f, 0.0f, 0.0f), tang(0.0f, 0.0f, 0.0f);
BVHTreeFromMesh treeData = {NULL};
bvhtree_from_mesh_looptri(&treeData, dm, 0.0, 2, 6);
if (treeData.tree) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(treeData.tree, &vec.x, &nearest, treeData.nearest_callback, &treeData);
if (nearest.index != -1) {
copy_v3_v3(&pos.x, nearest.co);
copy_v3_v3(&nor.x, nearest.no);
BLI_space_transform_apply(&transform, &pos.x);
BLI_space_transform_apply_normal(&transform, &nor.x);
// TODO tangent is still undefined
}
}
stack_store_float3(stack, offset_position, pos);
stack_store_float3(stack, offset_normal, nor);
stack_store_float3(stack, offset_tangent, tang);
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_MESH_H__ */

View File

@@ -0,0 +1,531 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_EVAL_TEXTURE_H__
#define __BVM_EVAL_TEXTURE_H__
/** \file bvm_eval_texture.h
* \ingroup bvm
*/
extern "C" {
#include "BLI_noise.h"
}
#include "bvm_eval_common.h"
namespace blenvm {
inline void eval_op_tex_proc_voronoi(EvalStack *stack, int distance_metric, int color_type,
StackIndex iMinkowskiExponent, StackIndex iScale,
StackIndex iNoiseSize, StackIndex iNabla,
StackIndex iW1, StackIndex iW2, StackIndex iW3, StackIndex iW4,
StackIndex iPos,
StackIndex oIntensity, StackIndex oColor, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float mexp = stack_load_float(stack, iMinkowskiExponent);
float noisesize = stack_load_float(stack, iNoiseSize);
float nabla = stack_load_float(stack, iNabla);
float w1 = stack_load_float(stack, iW1);
float w2 = stack_load_float(stack, iW2);
float w3 = stack_load_float(stack, iW3);
float w4 = stack_load_float(stack, iW4);
float aw1 = fabsf(w1);
float aw2 = fabsf(w2);
float aw3 = fabsf(w3);
float aw4 = fabsf(w4);
float sc = (aw1 + aw2 + aw3 + aw4);
if (sc != 0.0f)
sc = stack_load_float(stack, iScale) / sc;
float da[4], pa[12]; /* distance and point coordinate arrays of 4 nearest neighbors */
voronoi(texvec.x, texvec.y, texvec.z, da, pa, mexp, distance_metric);
float intensity = sc * fabsf(w1*da[0] + w2*da[1] + w3*da[2] + w4*da[3]);
float4 color;
float3 normal;
if (color_type == 0) {
color = float4(intensity, intensity, intensity, 1.0f);
}
else {
float ca[3]; /* cell color */
float r, g, b;
cellNoiseV(pa[0], pa[1], pa[2], ca);
r = aw1*ca[0];
g = aw1*ca[1];
b = aw1*ca[2];
cellNoiseV(pa[3], pa[4], pa[5], ca);
r += aw2*ca[0];
g += aw2*ca[1];
b += aw2*ca[2];
cellNoiseV(pa[6], pa[7], pa[8], ca);
r += aw3*ca[0];
g += aw3*ca[1];
b += aw3*ca[2];
cellNoiseV(pa[9], pa[10], pa[11], ca);
r += aw4*ca[0];
g += aw4*ca[1];
b += aw4*ca[2];
if (color_type > 1) {
float t1 = (da[1] - da[0]) * 10;
if (t1 > 1.0f)
t1 = 1.0f;
if (color_type > 2)
t1 *= intensity;
else
t1 *= sc;
r *= t1;
g *= t1;
b *= t1;
}
else {
r *= sc;
g *= sc;
b *= sc;
}
color = float4(r, g, b, 1.0f);
}
/* calculate bumpnormal */
{
float offs = nabla / noisesize; /* also scaling of texvec */
voronoi(texvec.x + offs, texvec.y, texvec.z, da, pa, mexp, distance_metric);
normal.x = sc * fabsf(w1*da[0] + w2*da[1] + w3*da[2] + w4*da[3]);
voronoi(texvec.x, texvec.y + offs, texvec.z, da, pa, mexp, distance_metric);
normal.y = sc * fabsf(w1*da[0] + w2*da[1] + w3*da[2] + w4*da[3]);
voronoi(texvec.x, texvec.y, texvec.z + offs, da, pa, mexp, distance_metric);
normal.z = sc * fabsf(w1*da[0] + w2*da[1] + w3*da[2] + w4*da[3]);
}
stack_store_float(stack, oIntensity, intensity);
stack_store_float4(stack, oColor, color);
stack_store_float3(stack, oNormal, normal);
}
inline void eval_op_tex_proc_clouds(EvalStack *stack,
StackIndex iPos, StackIndex iNabla, StackIndex iSize,
int depth, int noise_basis, int noise_hard,
StackIndex oIntensity, StackIndex oColor, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float size = stack_load_float(stack, iSize);
float nabla = stack_load_float(stack, iNabla);
float intensity = BLI_gTurbulence(size, texvec.x, texvec.y, texvec.z, depth, noise_hard, noise_basis);
float4 color;
float3 normal;
/* calculate bumpnormal */
{
float x = BLI_gTurbulence(size, texvec.x + nabla, texvec.y, texvec.z, depth, noise_hard, noise_basis);
float y = BLI_gTurbulence(size, texvec.x, texvec.y + nabla, texvec.z, depth, noise_hard, noise_basis);
float z = BLI_gTurbulence(size, texvec.x, texvec.y, texvec.z + nabla, depth, noise_hard, noise_basis);
normal = float3(x, y, z);
}
{
/* in this case, int. value should really be computed from color,
* and bumpnormal from that, would be too slow, looks ok as is */
float r = intensity;
float g = BLI_gTurbulence(size, texvec.y, texvec.x, texvec.z, depth, noise_hard, noise_basis);
float b = BLI_gTurbulence(size, texvec.y, texvec.z, texvec.x, depth, noise_hard, noise_basis);
color = float4(r, g, b, 1.0f);
}
stack_store_float(stack, oIntensity, intensity);
stack_store_float4(stack, oColor, color);
stack_store_float3(stack, oNormal, normal);
}
/* creates a sine wave */
static float tex_sin(float a)
{
a = 0.5f + 0.5f * sinf(a);
return a;
}
/* creates a saw wave */
static float tex_saw(float a)
{
const float b = 2*M_PI;
int n = (int)(a / b);
a -= n*b;
if (a < 0) a += b;
return a / b;
}
/* creates a triangle wave */
static float tex_tri(float a)
{
const float b = 2*M_PI;
const float rmax = 1.0;
a = rmax - 2.0f*fabsf(floorf((a*(1.0f/b))+0.5f) - (a*(1.0f/b)));
return a;
}
/* computes basic wood intensity value at x,y,z */
static float wood_int(float size, float x, float y, float z, float turb,
int noise_basis, int noise_basis_2, int noise_hard,
int wood_type)
{
float wi = 0;
float (*waveform[3])(float); /* create array of pointers to waveform functions */
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
waveform[1] = tex_saw;
waveform[2] = tex_tri;
/* check to be sure noise_basis_2 is initialized ahead of time */
if ((noise_basis_2 > 2) || (noise_basis_2 < 0)) noise_basis_2 = 0;
switch (wood_type) {
case 0: // TEX_BAND
wi = waveform[noise_basis_2]((x + y + z)*10.0f);
break;
case 1: // TEX_RING
wi = waveform[noise_basis_2](sqrtf(x*x + y*y + z*z)*20.0f);
break;
case 2: // TEX_BANDNOISE
wi = turb * BLI_gNoise(size, x, y, z, noise_hard, noise_basis);
wi = waveform[noise_basis_2]((x + y + z)*10.0f + wi);
break;
case 3: // TEX_RINGNOISE
wi = turb * BLI_gNoise(size, x, y, z, noise_hard, noise_basis);
wi = waveform[noise_basis_2](sqrtf(x*x + y*y + z*z)*20.0f + wi);
break;
}
return wi;
}
inline void eval_op_tex_proc_wood(EvalStack *stack,
StackIndex iPos, StackIndex iNabla, StackIndex iSize, StackIndex iTurb,
int noise_basis, int noise_basis_2, int noise_hard, int wood_type,
StackIndex oIntensity, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float size = stack_load_float(stack, iSize);
float nabla = stack_load_float(stack, iNabla);
float turb = stack_load_float(stack, iTurb);
float intensity = wood_int(size, texvec.x, texvec.y, texvec.z, turb, noise_basis, noise_basis_2, noise_hard, wood_type);
float3 normal;
/* calculate bumpnormal */
{
float x = wood_int(size, texvec.x + nabla, texvec.y, texvec.z, turb, noise_basis, noise_basis_2, noise_hard, wood_type);
float y = wood_int(size, texvec.x, texvec.y + nabla, texvec.z, turb, noise_basis, noise_basis_2, noise_hard, wood_type);
float z = wood_int(size, texvec.x, texvec.y, texvec.z + nabla, turb, noise_basis, noise_basis_2, noise_hard, wood_type);
normal = float3(x, y, z);
}
stack_store_float(stack, oIntensity, intensity);
stack_store_float3(stack, oNormal, normal);
}
/* computes basic marble intensity at x,y,z */
static float marble_int(float size, float x, float y, float z, float turb,
int depth, int noise_basis, int noise_basis_2,
int noise_hard, int marble_type)
{
float (*waveform[3])(float); /* create array of pointers to waveform functions */
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
waveform[1] = tex_saw;
waveform[2] = tex_tri;
/* check to be sure noise_basis_2 isn't initialized ahead of time */
if ((noise_basis_2 > 2) || (noise_basis_2 < 0))
noise_basis_2 = 0;
float n = 5.0f * (x + y + z);
float intensity = n + turb * BLI_gTurbulence(size, x, y, z, depth, noise_hard, noise_basis);
if (marble_type >=0 ) { /* TEX_SOFT always true */
intensity = waveform[noise_basis_2](intensity);
if (marble_type == 1) {
intensity = sqrtf(intensity);
}
else if (marble_type == 2) {
intensity = sqrtf(sqrtf(intensity));
}
}
return intensity;
}
inline void eval_op_tex_proc_marble(EvalStack *stack,
StackIndex iPos, StackIndex iNabla, StackIndex iSize, StackIndex iTurb,
int depth, int noise_basis, int noise_basis_2,
int noise_hard, int marble_type,
StackIndex oIntensity, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float size = stack_load_float(stack, iSize);
float nabla = stack_load_float(stack, iNabla);
float turb = stack_load_float(stack, iTurb);
float intensity = marble_int(size, texvec.x, texvec.y, texvec.z, turb, depth, noise_basis, noise_basis_2, noise_hard, marble_type);
float3 normal;
/* calculate bumpnormal */
{
float x = marble_int(size, texvec.x + nabla, texvec.y, texvec.z, turb, depth, noise_basis, noise_basis_2, noise_hard, marble_type);
float y = marble_int(size, texvec.x, texvec.y + nabla, texvec.z, turb, depth, noise_basis, noise_basis_2, noise_hard, marble_type);
float z = marble_int(size, texvec.x, texvec.y, texvec.z + nabla, turb, depth, noise_basis, noise_basis_2, noise_hard, marble_type);
normal = float3(x, y, z);
}
stack_store_float(stack, oIntensity, intensity);
stack_store_float3(stack, oNormal, normal);
}
inline void eval_op_tex_proc_musgrave(EvalStack *stack,
StackIndex iPos, StackIndex iNabla, StackIndex iSize,
StackIndex iDim, StackIndex iLac, StackIndex iOct,
StackIndex iInt, StackIndex iOffset, StackIndex iGain,
int noise_basis, int noise_type,
StackIndex oIntensity, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float size = stack_load_float(stack, iSize);
float nabla = stack_load_float(stack, iNabla);
float dimension = stack_load_float(stack, iDim);
float lacunarity = stack_load_float(stack, iLac);
float octaves = stack_load_float(stack, iOct);
float nintensity = stack_load_float(stack, iInt);
float offset = stack_load_float(stack, iOffset);
float gain = stack_load_float(stack, iGain);
float intensity, x, y, z;
float offs = nabla / size; /* also scaling of texvec */
switch (noise_type) {
case 0: /* TEX_MFRACTAL */
case 3: /* TEX_FBM */ {
float (*mgravefunc)(float, float, float, float, float, float, int);
if (noise_type == 0)
mgravefunc = mg_MultiFractal;
else
mgravefunc = mg_fBm;
intensity = nintensity*mgravefunc(texvec.x, texvec.y, texvec.z, dimension, lacunarity, octaves, noise_basis);
/* calculate bumpnormal */
x = nintensity*mgravefunc(texvec.x + offs, texvec.y, texvec.z, dimension, lacunarity, octaves, noise_basis);
y = nintensity*mgravefunc(texvec.x, texvec.y + offs, texvec.z, dimension, lacunarity, octaves, noise_basis);
z = nintensity*mgravefunc(texvec.x, texvec.y, texvec.z + offs, dimension, lacunarity, octaves, noise_basis);
break;
}
case 1: /* TEX_RIDGEDMF */
case 2: /* TEX_HYBRIDMF */ {
float (*mgravefunc)(float, float, float, float, float, float, float, float, int);
if (noise_type == 2)
mgravefunc = mg_RidgedMultiFractal;
else
mgravefunc = mg_HybridMultiFractal;
intensity = nintensity*mgravefunc(texvec.x, texvec.y, texvec.z, dimension, lacunarity, octaves, offset, gain, noise_basis);
/* calculate bumpnormal */
x = nintensity*mgravefunc(texvec.x + offs, texvec.y, texvec.z, dimension, lacunarity, octaves, offset, gain, noise_basis);
y = nintensity*mgravefunc(texvec.x, texvec.y + offs, texvec.z, dimension, lacunarity, octaves, offset, gain, noise_basis);
z = nintensity*mgravefunc(texvec.x, texvec.y, texvec.z + offs, dimension, lacunarity, octaves, offset, gain, noise_basis);
break;
}
case 4: /* TEX_HTERRAIN */ {
intensity = nintensity*mg_HeteroTerrain(texvec.x, texvec.y, texvec.z, dimension, lacunarity, octaves, offset, noise_basis);
/* calculate bumpnormal */
x = nintensity*mg_HeteroTerrain(texvec.x + offs, texvec.y, texvec.z, dimension, lacunarity, octaves, offset, noise_basis);
y = nintensity*mg_HeteroTerrain(texvec.x, texvec.y + offs, texvec.z, dimension, lacunarity, octaves, offset, noise_basis);
z = nintensity*mg_HeteroTerrain(texvec.x, texvec.y, texvec.z + offs, dimension, lacunarity, octaves, offset, noise_basis);
break;
}
}
float3 normal = float3(x, y, z);
stack_store_float(stack, oIntensity, intensity);
stack_store_float3(stack, oNormal, normal);
}
inline void eval_op_tex_proc_magic(EvalStack *stack,
StackIndex iPos, StackIndex iTurb,
int depth,
StackIndex oIntensity, StackIndex oColor, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float turbulence = stack_load_float(stack, iTurb);
float turb = turbulence / 5.0f;
float x = sinf(( texvec[0] + texvec[1] + texvec[2]) * 5.0f);
float y = cosf((-texvec[0] + texvec[1] - texvec[2]) * 5.0f);
float z = -cosf((-texvec[0] - texvec[1] + texvec[2]) * 5.0f);
if (depth>0) {
x *= turb;
y *= turb;
z *= turb;
y = -cosf(x-y+z);
y *= turb;
if (depth>1) {
x= cosf(x-y-z);
x*= turb;
if (depth>2) {
z= sinf(-x-y-z);
z*= turb;
if (depth>3) {
x= -cosf(-x+y-z);
x*= turb;
if (depth>4) {
y= -sinf(-x+y+z);
y*= turb;
if (depth>5) {
y= -cosf(-x+y+z);
y*= turb;
if (depth>6) {
x= cosf(x+y+z);
x*= turb;
if (depth>7) {
z= sinf(x+y-z);
z*= turb;
if (depth>8) {
x= -cosf(-x-y+z);
x*= turb;
if (depth>9) {
y= -sinf(x-y+z);
y*= turb;
}
}
}
}
}
}
}
}
}
}
if (turb != 0.0f) {
turb *= 2.0f;
x /= turb;
y /= turb;
z /= turb;
}
float3 normal(x, y, z);
float4 color(0.5f - x, 0.5f - y, 0.5f - z, 1.0f);
float intensity = (1.0f / 3.0f) * (color.x + color.y + color.z);
stack_store_float(stack, oIntensity, intensity);
stack_store_float4(stack, oColor, color);
stack_store_float3(stack, oNormal, normal);
}
inline void eval_op_tex_proc_stucci(EvalStack *stack,
StackIndex iPos, StackIndex iSize, StackIndex iTurb,
int noise_basis, int noisehard, int noise_type,
StackIndex oIntensity, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float noisesize = stack_load_float(stack, iSize);
float turbulence = stack_load_float(stack, iTurb);
float b2 = BLI_gNoise(noisesize, texvec[0], texvec[1], texvec[2], noisehard, noise_basis);
float offset = turbulence / 200.0f;
if (noise_type)
offset *= (b2 * b2);
float x = BLI_gNoise(noisesize, texvec[0] + offset, texvec[1], texvec[2], noisehard, noise_basis);
float y = BLI_gNoise(noisesize, texvec[0], texvec[1] + offset, texvec[2], noisehard, noise_basis);
float z = BLI_gNoise(noisesize, texvec[0], texvec[1], texvec[2] + offset, noisehard, noise_basis);
float intensity = z;
if (noise_type == 2) { /* TEX_WALLOUT */
x = -x;
y = -y;
z = -z;
intensity = 1.0f - intensity;
}
if (intensity < 0.0f)
intensity = 0.0f;
float3 normal(x, y, z);
stack_store_float(stack, oIntensity, intensity);
stack_store_float3(stack, oNormal, normal);
}
inline void eval_op_tex_proc_distnoise(EvalStack *stack,
StackIndex iPos, StackIndex iSize,
StackIndex iNabla, StackIndex iDist,
int noise_basis, int noise_basis_2,
StackIndex oIntensity, StackIndex oNormal)
{
float3 texvec = stack_load_float3(stack, iPos);
float noisesize = stack_load_float(stack, iSize);
float nabla = stack_load_float(stack, iNabla);
float dist_amount = stack_load_float(stack, iDist);
float intensity = mg_VLNoise(texvec[0], texvec[1], texvec[2], dist_amount, noise_basis, noise_basis_2);
/* calculate bumpnormal */
float offs = nabla / noisesize; /* also scaling of texvec */
float x = mg_VLNoise(texvec[0] + offs, texvec[1], texvec[2], dist_amount, noise_basis, noise_basis_2);
float y = mg_VLNoise(texvec[0], texvec[1] + offs, texvec[2], dist_amount, noise_basis, noise_basis_2);
float z = mg_VLNoise(texvec[0], texvec[1], texvec[2] + offs, dist_amount, noise_basis, noise_basis_2);
float3 normal(x, y, z);
stack_store_float(stack, oIntensity, intensity);
stack_store_float3(stack, oNormal, normal);
}
} /* namespace blenvm */
#endif /* __BVM_EVAL_TEXTURE_H__ */

View File

@@ -0,0 +1,120 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file bvm_function.cc
* \ingroup bvm
*/
#include "bvm_eval.h"
#include "bvm_function.h"
namespace blenvm {
FunctionBVM::FunctionBVM()
{
}
FunctionBVM::~FunctionBVM()
{
}
size_t FunctionBVM::num_arguments() const
{
return m_arguments.size();
}
const Argument &FunctionBVM::argument(size_t index) const
{
return m_arguments[index];
}
const Argument &FunctionBVM::argument(const string &name) const
{
for (ArgumentList::const_iterator it = m_arguments.begin(); it != m_arguments.end(); ++it)
if ((*it).name == name)
return *it;
return *(m_arguments.end());
}
size_t FunctionBVM::num_return_values() const
{
return m_return_values.size();
}
const Argument &FunctionBVM::return_value(size_t index) const
{
return m_return_values[index];
}
const Argument &FunctionBVM::return_value(const string &name) const
{
for (ArgumentList::const_iterator it = m_return_values.begin(); it != m_return_values.end(); ++it)
if ((*it).name == name)
return *it;
return *(m_return_values.end());
}
void FunctionBVM::add_argument(const TypeDesc &typedesc, const string &name, StackIndex stack_offset)
{
m_arguments.push_back(Argument(typedesc, name, stack_offset));
}
void FunctionBVM::add_return_value(const TypeDesc &typedesc, const string &name, StackIndex stack_offset)
{
m_return_values.push_back(Argument(typedesc, name, stack_offset));
}
void FunctionBVM::eval(EvalContext *context, const EvalGlobals *globals, const void *arguments[], void *results[]) const
{
#if 0
EvalStack stack[BVM_STACK_SIZE] = {0};
/* initialize input arguments */
for (int i = 0; i < num_arguments(); ++i) {
const Argument &arg = argument(i);
if (arg.stack_offset != BVM_STACK_INVALID) {
EvalStack *value = &stack[arg.stack_offset];
const TypeSpec *typespec = arg.typedesc.get_typespec();
typespec->copy_value((void *)value, arguments[i]);
}
}
context->eval_instructions(globals, this, entry_point(), stack);
/* read out return values */
for (int i = 0; i < num_return_values(); ++i) {
const Argument &rval = return_value(i);
EvalStack *value = &stack[rval.stack_offset];
const TypeSpec *typespec = rval.typedesc.get_typespec();
typespec->copy_value(results[i], (void *)value);
}
#endif
}
} /* namespace blenvm */

View File

@@ -0,0 +1,94 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_FUNCTION_H__
#define __BVM_FUNCTION_H__
/** \file bvm_function.h
* \ingroup bvm
*/
#include <vector>
#include <stdint.h>
#include "MEM_guardedalloc.h"
#include "function.h"
#include "typedesc.h"
#include "bvm_instruction_list.h"
#include "util_opcode.h"
#include "util_string.h"
namespace blenvm {
struct EvalContext;
struct EvalGlobals;
struct Argument {
Argument(const TypeDesc &typedesc, const string &name, StackIndex stack_offset) :
typedesc(typedesc),
name(name),
stack_offset(stack_offset)
{}
TypeDesc typedesc;
string name;
StackIndex stack_offset;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:ReturnValue")
};
struct FunctionBVM : public FunctionBase, public InstructionList {
typedef std::vector<Argument> ArgumentList;
FunctionBVM();
~FunctionBVM();
size_t num_return_values() const;
const Argument &return_value(size_t index) const;
const Argument &return_value(const string &name) const;
size_t num_arguments() const;
const Argument &argument(size_t index) const;
const Argument &argument(const string &name) const;
void add_argument(const TypeDesc &typedesc, const string &name, StackIndex stack_offset);
void add_return_value(const TypeDesc &typedesc, const string &name, StackIndex stack_offset);
void eval(EvalContext *context, const EvalGlobals *globals, const void *arguments[], void *results[]) const;
private:
ArgumentList m_arguments;
ArgumentList m_return_values;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:FunctionBVM")
};
} /* namespace blenvm */
#endif /* __BVM_FUNCTION_H__ */

View File

@@ -0,0 +1,55 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file bvm_instruction_list.cc
* \ingroup bvm
*/
#include "bvm_instruction_list.h"
namespace blenvm {
InstructionList::InstructionList() :
m_entry_point(0)
{
}
InstructionList::~InstructionList()
{
}
void InstructionList::add_instruction(Instruction v)
{
m_instructions.push_back(v);
}
void InstructionList::set_entry_point(int entry_point)
{
m_entry_point = entry_point;
}
} /* namespace blenvm */

View File

@@ -0,0 +1,194 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BVM_INSTRUCTION_LIST_H__
#define __BVM_INSTRUCTION_LIST_H__
/** \file bvm_instruction_list.h
* \ingroup bvm
*/
#include <vector>
#include <stdint.h>
#include "MEM_guardedalloc.h"
#include "util_opcode.h"
#include "util_data_ptr.h"
#include "util_math.h"
#include "util_string.h"
#include "util_thread.h"
namespace blenvm {
typedef uint32_t Instruction;
typedef Instruction StackIndex;
#define BVM_STACK_INVALID 0xFFFFFFFF
#define BVM_JMP_INVALID 0xFFFFFFFF
static inline Instruction float_to_instruction(float f)
{
union { uint32_t i; float f; } u;
u.f = f;
return u.i;
}
static inline Instruction int_to_instruction(int v)
{
union { uint32_t i; int v; } u;
u.v = v;
return u.i;
}
static inline float instruction_to_float(Instruction i)
{
union { uint32_t i; float f; } u;
u.i = i;
return u.f;
}
static inline int instruction_to_int(Instruction i)
{
union { uint32_t i; int v; } u;
u.i = i;
return u.v;
}
struct InstructionList {
typedef std::vector<Instruction> Instructions;
InstructionList();
~InstructionList();
OpCode read_opcode(int *instr) const
{
OpCode op = (OpCode)m_instructions[*instr];
++(*instr);
return op;
}
StackIndex read_stack_index(int *instr) const
{
StackIndex index = m_instructions[*instr];
++(*instr);
return index;
}
int read_jump_address(int *instr) const
{
int address = m_instructions[*instr];
++(*instr);
return address;
}
float read_float(int *instr) const
{
float f = instruction_to_float(m_instructions[*instr]);
++(*instr);
return f;
}
float3 read_float3(int *instr) const
{
float3 f;
f.x = instruction_to_float(m_instructions[*instr + 0]);
f.y = instruction_to_float(m_instructions[*instr + 1]);
f.z = instruction_to_float(m_instructions[*instr + 2]);
(*instr) += 3;
return f;
}
float4 read_float4(int *instr) const
{
float4 f;
f.x = instruction_to_float(m_instructions[*instr + 0]);
f.y = instruction_to_float(m_instructions[*instr + 1]);
f.z = instruction_to_float(m_instructions[*instr + 2]);
f.w = instruction_to_float(m_instructions[*instr + 3]);
(*instr) += 4;
return f;
}
int read_int(int *instr) const
{
int i = instruction_to_int(m_instructions[*instr]);
++(*instr);
return i;
}
matrix44 read_matrix44(int *instr) const
{
matrix44 m;
m.data[0][0] = instruction_to_float(m_instructions[*instr + 0]);
m.data[0][1] = instruction_to_float(m_instructions[*instr + 1]);
m.data[0][2] = instruction_to_float(m_instructions[*instr + 2]);
m.data[0][3] = instruction_to_float(m_instructions[*instr + 3]);
m.data[1][0] = instruction_to_float(m_instructions[*instr + 4]);
m.data[1][1] = instruction_to_float(m_instructions[*instr + 5]);
m.data[1][2] = instruction_to_float(m_instructions[*instr + 6]);
m.data[1][3] = instruction_to_float(m_instructions[*instr + 7]);
m.data[2][0] = instruction_to_float(m_instructions[*instr + 8]);
m.data[2][1] = instruction_to_float(m_instructions[*instr + 9]);
m.data[2][2] = instruction_to_float(m_instructions[*instr + 10]);
m.data[2][3] = instruction_to_float(m_instructions[*instr + 11]);
m.data[3][0] = instruction_to_float(m_instructions[*instr + 12]);
m.data[3][1] = instruction_to_float(m_instructions[*instr + 13]);
m.data[3][2] = instruction_to_float(m_instructions[*instr + 14]);
m.data[3][3] = instruction_to_float(m_instructions[*instr + 15]);
(*instr) += 16;
return m;
}
const char *read_string(int *instr) const
{
const char *s = (const char *)(&m_instructions[*instr]);
const char *c = s;
while (true) {
++(*instr);
if (c[0]=='\0' || c[1]=='\0' || c[2]=='\0' || c[3]=='\0')
break;
c += 4;
}
return s;
}
void add_instruction(Instruction v);
int get_instruction_count() const { return m_instructions.size(); }
int entry_point() const { return m_entry_point; }
void set_entry_point(int entry_point);
protected:
Instructions m_instructions;
int m_entry_point;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:InstructionList")
};
} /* namespace blenvm */
#endif /* __BVM_INSTRUCTION_LIST_H__ */

View File

@@ -0,0 +1,52 @@
# ***** 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.
#
# The Original Code is Copyright (C) 2015, Blender Foundation
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): Lukas Toenne.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
..
../intern
../util
../../blenkernel
../../blenlib
../../makesdna
../../makesrna
../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
compiler.cc
compiler.h
node_graph.cc
node_graph.h
node_value.cc
node_value.h
typedesc.cc
typedesc.h
)
blender_add_lib(bf_blenvm_compile "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -0,0 +1,248 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/compile/compiler.cc
* \ingroup bvm
*/
#include <cstdio>
#include <set>
#include <sstream>
#include "compiler.h"
#include "node_graph.h"
#include "util_opcode.h"
namespace blenvm {
const ValueHandle VALUE_UNDEFINED = 0;
/* ------------------------------------------------------------------------- */
Scope::Scope(Scope *parent) :
parent(parent)
{
}
bool Scope::has_node(const NodeInstance *node) const
{
/* XXX this is not ideal, but we can expect all outputs
* to be mapped once a node is added.
*/
ConstOutputKey key(node, node->type->find_output(0));
return has_value(key);
}
bool Scope::has_value(const ConstOutputKey &key) const
{
const Scope *scope = this;
while (scope) {
SocketValueMap::const_iterator it = scope->values.find(key);
if (it != scope->values.end()) {
return true;
}
scope = scope->parent;
}
return false;
}
ValueHandle Scope::find_value(const ConstOutputKey &key) const
{
const Scope *scope = this;
while (scope) {
SocketValueMap::const_iterator it = scope->values.find(key);
if (it != scope->values.end()) {
return it->second;
}
scope = scope->parent;
}
BLI_assert(false && "Value not defined in any scope!");
return ValueHandle(0);
}
void Scope::set_value(const ConstOutputKey &key, ValueHandle value)
{
bool ok = values.insert(SocketValueMap::value_type(key, value)).second;
BLI_assert(ok && "Could not insert socket value!");
UNUSED_VARS(ok);
}
/* ------------------------------------------------------------------------- */
Compiler::Compiler(CodeGenerator *codegen) :
m_codegen(codegen)
{
}
Compiler::~Compiler()
{
}
void Compiler::compile_node_graph(const string &name, const NodeGraph &graph)
{
m_codegen->node_graph_begin(name, &graph, true);
compile_node_statements(graph);
m_codegen->node_graph_end();
m_codegen->finalize_function();
}
void Compiler::debug_node_graph(const string &name, const NodeGraph &graph, FILE *file)
{
m_codegen->node_graph_begin(name, &graph, true);
compile_node_statements(graph);
m_codegen->node_graph_end();
m_codegen->debug_function(file);
}
/* Compile nodes as a simple expression.
* Every node can be treated as a single statement. Each node is translated
* into a function call, with regular value arguments. The resulting value is
* assigned to a variable and can be used for subsequent node function calls.
*/
void Compiler::compile_node_statements(const NodeGraph &graph)
{
/* cache function arguments */
int num_inputs = graph.inputs.size();
int num_outputs = graph.outputs.size();
for (int i = 0; i < num_inputs; ++i) {
const NodeGraph::Input &input = graph.inputs[i];
const TypeSpec *typespec = input.typedesc.get_typespec();
if (input.key) {
ValueHandle handle = m_codegen->map_argument(i, typespec);
m_argument_values.insert(ArgumentValueMap::value_type(input.key, handle));
}
}
Scope scope_main(NULL);
for (int i = 0; i < num_outputs; ++i) {
const NodeGraph::Output &output = graph.outputs[i];
const TypeSpec *typespec = output.typedesc.get_typespec();
expand_node(output.key.node, scope_main);
ValueHandle value = scope_main.find_value(output.key);
m_codegen->store_return_value(i, typespec, value);
}
}
void Compiler::expand_node(const NodeInstance *node, Scope &scope)
{
if (scope.has_node(node))
return;
switch (node->type->kind()) {
case NODE_TYPE_FUNCTION:
case NODE_TYPE_KERNEL:
expand_expression_node(node, scope);
break;
case NODE_TYPE_PASS:
expand_pass_node(node, scope);
break;
case NODE_TYPE_ARG:
expand_argument_node(node, scope);
break;
}
}
void Compiler::expand_pass_node(const NodeInstance *node, Scope &scope)
{
BLI_assert(node->num_inputs() == 1);
BLI_assert(node->num_outputs() == 1);
ConstInputKey input = node->input(0);
BLI_assert(input.value_type() == INPUT_EXPRESSION);
expand_node(input.link().node, scope);
}
void Compiler::expand_argument_node(const NodeInstance *node, Scope &scope)
{
BLI_assert(node->num_outputs() == 1);
ConstOutputKey output = node->output(0);
scope.set_value(output, m_argument_values.at(output));
}
void Compiler::expand_expression_node(const NodeInstance *node, Scope &scope)
{
/* function call arguments */
std::vector<ValueHandle> input_args, output_args;
for (int i = 0; i < node->num_outputs(); ++i) {
ConstOutputKey output = node->output(i);
const TypeSpec *typespec = output.socket->typedesc.get_typespec();
ValueHandle value = m_codegen->alloc_node_value(typespec,
output.node->name + "__" + output.socket->name);
output_args.push_back(value);
scope.set_value(output, value);
}
/* set input arguments */
for (int i = 0; i < node->num_inputs(); ++i) {
ConstInputKey input = node->input(i);
const TypeSpec *typespec = input.socket->typedesc.get_typespec();
switch (input.value_type()) {
case INPUT_CONSTANT: {
ValueHandle value = m_codegen->create_constant(typespec, input.value());
input_args.push_back(value);
break;
}
case INPUT_EXPRESSION: {
expand_node(input.link().node, scope);
ValueHandle link_value = scope.find_value(input.link());
input_args.push_back(link_value);
break;
}
case INPUT_VARIABLE: {
/* TODO */
BLI_assert(false && "Variable inputs not supported yet!");
break;
}
}
}
m_codegen->eval_node(node->type, input_args, output_args);
}
} /* namespace blenvm */

View File

@@ -0,0 +1,113 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __COMPILER_H__
#define __COMPILER_H__
/** \file blender/blenvm/compile/compiler.h
* \ingroup bvm
*/
#include <set>
#include <vector>
#include "MEM_guardedalloc.h"
#include "node_graph.h"
#include "util_array.h"
#include "util_opcode.h"
#include "util_string.h"
#include "util_math.h"
namespace blenvm {
struct NodeGraph;
struct NodeInstance;
struct TypeDesc;
typedef void* ValueHandle;
extern const ValueHandle VALUE_UNDEFINED;
typedef std::map<ConstOutputKey, ValueHandle> SocketValueMap;
struct Scope {
Scope(Scope *parent);
bool has_node(const NodeInstance *node) const;
bool has_value(const ConstOutputKey &key) const;
ValueHandle find_value(const ConstOutputKey &key) const;
void set_value(const ConstOutputKey &key, ValueHandle value);
Scope *parent;
SocketValueMap values;
};
struct CodeGenerator {
virtual void finalize_function() = 0;
virtual void debug_function(FILE *file) = 0;
virtual void node_graph_begin(const string &name, const NodeGraph *graph, bool use_globals) = 0;
virtual void node_graph_end() = 0;
virtual void store_return_value(size_t output_index, const TypeSpec *typespec, ValueHandle value) = 0;
virtual ValueHandle map_argument(size_t input_index, const TypeSpec *typespec) = 0;
virtual ValueHandle alloc_node_value(const TypeSpec *typespec, const string &name) = 0;
virtual ValueHandle create_constant(const TypeSpec *typespec, const NodeConstant *node_value) = 0;
virtual void eval_node(const NodeType *nodetype,
ArrayRef<ValueHandle> input_args,
ArrayRef<ValueHandle> output_args) = 0;
};
struct Compiler {
typedef std::map<ConstOutputKey, ValueHandle> ArgumentValueMap;
Compiler(CodeGenerator *codegen);
~Compiler();
void compile_node_graph(const string &name, const NodeGraph &graph);
void debug_node_graph(const string &name, const NodeGraph &graph, FILE *file);
protected:
void compile_node_statements(const NodeGraph &graph);
void expand_node(const NodeInstance *node, Scope &scope);
void expand_pass_node(const NodeInstance *node, Scope &scope);
void expand_argument_node(const NodeInstance *node, Scope &scope);
void expand_expression_node(const NodeInstance *node, Scope &scope);
private:
CodeGenerator *m_codegen;
ArgumentValueMap m_argument_values;
};
} /* namespace blenvm */
#endif /* __LLVM_COMPILER_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,474 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/node_graph.h
* \ingroup bvm
*/
#ifndef __BVM_NODEGRAPH_H__
#define __BVM_NODEGRAPH_H__
#include <stdlib.h>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <set>
#include <list>
#include "MEM_guardedalloc.h"
extern "C" {
#include "BLI_utildefines.h"
#if 0
#include "DNA_node_types.h"
#include "BKE_node.h"
#endif
}
#include "node_value.h"
#include "util_array.h"
#include "util_opcode.h"
#include "util_string.h"
namespace blenvm {
struct NodeGraph;
struct NodeType;
struct NodeInstance;
struct NodeBlock;
struct NodeInput {
NodeInput(const string &name,
const TypeDesc &typedesc,
NodeConstant *default_value,
BVMInputValueType value_type);
~NodeInput();
string name;
TypeDesc typedesc;
NodeConstant *default_value;
BVMInputValueType value_type;
};
struct NodeOutput {
NodeOutput(const string &name,
const TypeDesc &typedesc,
BVMOutputValueType value_type);
~NodeOutput();
string name;
TypeDesc typedesc;
BVMOutputValueType value_type;
};
/** Special kinds of node types */
enum eNodeTypeKind {
NODE_TYPE_FUNCTION, /* regular function call node */
NODE_TYPE_KERNEL, /* kernel nodes can define local variables */
NODE_TYPE_PASS, /* pass nodes are skipped */
NODE_TYPE_ARG, /* arg nodes represent input values */
};
struct NodeType {
typedef std::vector<NodeInput> InputList;
typedef std::vector<NodeOutput> OutputList;
NodeType(const string &name, eNodeTypeKind kind = NODE_TYPE_FUNCTION, bool use_globals = false);
~NodeType();
const string &name() const { return m_name; }
eNodeTypeKind kind() const { return m_kind; }
bool use_globals() const { return m_use_globals; }
void set_use_globals(bool use_globals) { m_use_globals = use_globals; }
int num_inputs() const { return m_inputs.size(); }
int num_outputs() const { return m_outputs.size(); }
const NodeInput *find_input(int index) const;
const NodeOutput *find_output(int index) const;
const NodeInput *find_input(const string &name) const;
const NodeOutput *find_output(const string &name) const;
/* stub implementation in case socket is passed directly */
const NodeInput *find_input(const NodeInput *socket) const;
const NodeOutput *find_output(const NodeOutput *socket) const;
// bool verify_argument_socket(NodeSocket &socket, Type *type, int index,
// Module *module, LLVMContext &context, raw_ostream &err);
// bool verify_arguments(Module *module, LLVMContext &context, raw_ostream &err);
const NodeInput *add_input(const string &name,
const string &type,
NodeConstant *default_value,
BVMInputValueType value_type = INPUT_EXPRESSION);
const NodeOutput *add_output(const string &name,
const string &type,
BVMOutputValueType value_type = OUTPUT_EXPRESSION);
template <typename T>
const NodeInput *add_input(const string &name,
const string &type,
T default_value,
BVMInputValueType value_type = INPUT_EXPRESSION);
private:
string m_name;
InputList m_inputs;
OutputList m_outputs;
eNodeTypeKind m_kind;
bool m_use_globals;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:NodeType")
};
struct ConstOutputKey {
ConstOutputKey();
ConstOutputKey(const NodeInstance *node, const string &socket);
ConstOutputKey(const NodeInstance *node, const NodeOutput *socket);
bool operator < (const ConstOutputKey &other) const;
operator bool() const;
BVMOutputValueType value_type() const;
const NodeInstance *node;
const NodeOutput *socket;
};
struct OutputKey {
OutputKey();
OutputKey(NodeInstance *node, const string &socket);
OutputKey(NodeInstance *node, const NodeOutput *socket);
operator ConstOutputKey() const;
bool operator < (const OutputKey &other) const;
operator bool() const;
BVMOutputValueType value_type() const;
NodeInstance *node;
const NodeOutput *socket;
};
struct ConstInputKey {
ConstInputKey();
ConstInputKey(const NodeInstance *node, const string &socket);
ConstInputKey(const NodeInstance *node, const NodeInput *socket);
bool operator < (const ConstInputKey &other) const;
operator bool() const;
ConstOutputKey link() const;
const NodeConstant *value() const;
BVMInputValueType value_type() const;
const NodeInstance *node;
const NodeInput *socket;
};
struct InputKey {
InputKey();
InputKey(NodeInstance *node, const string &socket);
InputKey(NodeInstance *node, const NodeInput *socket);
operator ConstInputKey() const;
bool operator < (const InputKey &other) const;
operator bool() const;
OutputKey link() const;
void link_set(const OutputKey &from) const;
const NodeConstant *value() const;
void value_set(NodeConstant *value) const;
BVMInputValueType value_type() const;
NodeInstance *node;
const NodeInput *socket;
};
typedef std::set<ConstInputKey> InputSet;
typedef std::set<ConstOutputKey> OutputSet;
#if 0
typedef std::map<string, OutputKey> VariableMap;
#endif
struct NodeInstance {
struct InputInstance {
InputInstance() :
value(NULL)
{}
OutputKey link;
NodeConstant *value;
};
typedef std::map<string, InputInstance> InputMap;
typedef std::pair<string, InputInstance> InputPair;
NodeInstance(const NodeType *type, const string &name);
NodeInstance(const NodeInstance *other, const string &name);
~NodeInstance();
InputKey input(const string &name);
InputKey input(int index);
ConstInputKey input(const string &name) const;
ConstInputKey input(int index) const;
OutputKey output(const string &name);
OutputKey output(int index);
ConstOutputKey output(const string &name) const;
ConstOutputKey output(int index) const;
int num_inputs() const { return type->num_inputs(); }
int num_outputs() const { return type->num_outputs(); }
OutputKey link(const string &name) const;
OutputKey link(int index) const;
bool link_set(const string &name, const OutputKey &from);
const NodeConstant *input_value(const string &name) const;
const NodeConstant *input_value(int index) const;
bool input_value_set(const string &name, NodeConstant *value);
template <typename T>
bool input_value_set(const string &name, const T &value)
{
const NodeInput *socket = type->find_input(name);
return socket ? input_value_set(name, NodeConstant::create(socket->typedesc, value)) : false;
}
const NodeType *type;
string name;
InputMap inputs;
int index; /* ordering index */
const NodeBlock *block;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:NodeInstance")
};
typedef std::set<NodeInstance*> NodeSet;
typedef std::map<const NodeInstance*, NodeInstance*> NodeMap;
typedef std::map<ConstOutputKey, OutputKey> OutputMap;
struct NodeIndexCmp {
bool operator () (const NodeInstance *a, const NodeInstance *b) const
{
return a->index < b->index;
}
};
typedef std::set<const NodeInstance *, NodeIndexCmp> OrderedNodeSet;
struct NodeBlock {
NodeBlock(const string &name, NodeBlock *parent = NULL);
const string &name() const { return m_name; }
NodeBlock *parent() const { return m_parent; }
void parent_set(NodeBlock *parent) { m_parent = parent; }
NodeSet &nodes() { return m_nodes; }
const NodeSet &nodes() const { return m_nodes; }
#if 0 /* unused */
void prune(const NodeSet &used_nodes);
#endif
private:
string m_name;
NodeBlock *m_parent;
NodeSet m_nodes;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:NodeBlock")
};
typedef std::set<NodeBlock *> NodeBlockSet;
/** Formal input parameter of a NodeGraph. */
struct NodeInputParam {
NodeInputParam(const string &name, const string &type) :
name(name), type(type)
{}
string name;
string type;
};
/** Formal output parameter of a NodeGraph.
* \note The parameter takes ownership of the default_value!
*/
struct NodeOutputParam {
NodeOutputParam(const string &name, const string &type, NodeConstant *default_value) :
name(name), type(type), default_value(default_value)
{}
NodeOutputParam(const NodeOutputParam &other) :
name(other.name), type(other.type), default_value(other.default_value->copy())
{}
~NodeOutputParam()
{
if (default_value)
delete default_value;
}
NodeOutputParam& operator = (const NodeOutputParam &other)
{
name = other.name;
type = other.type;
default_value = other.default_value->copy();
return *this;
}
template <typename T>
NodeOutputParam(const string &name, const string &type, const T &default_value) :
name(name), type(type), default_value(NodeConstant::create(TypeDesc(type), default_value))
{}
string name;
string type;
NodeConstant *default_value;
};
struct NodeGraph {
struct Input {
Input(const string &name, const TypeDesc &typedesc, const OutputKey &key) :
name(name), typedesc(typedesc), key(key) {}
string name;
TypeDesc typedesc;
OutputKey key;
};
struct Output {
Output(const string &name, const TypeDesc &typedesc, const OutputKey &key) :
name(name), typedesc(typedesc), key(key) {}
string name;
TypeDesc typedesc;
OutputKey key;
};
typedef std::vector<Input> InputList;
typedef std::vector<Output> OutputList;
typedef std::map<string, TypeDesc> TypeDefMap;
typedef std::pair<string, TypeDesc> TypeDefPair;
typedef std::map<string, NodeType> NodeTypeMap;
typedef std::pair<string, NodeType> NodeTypeMapPair;
typedef std::map<string, NodeInstance*> NodeInstanceMap;
typedef std::pair<string, NodeInstance*> NodeInstanceMapPair;
typedef std::list<NodeBlock> NodeBlockList;
static NodeTypeMap node_types;
static const NodeType *find_node_type(const string &name);
static NodeType *add_node_type(const string &name, eNodeTypeKind kind = NODE_TYPE_FUNCTION);
static void remove_node_type(const string &name);
NodeGraph();
NodeGraph(ArrayRef<NodeInputParam> inputs, ArrayRef<NodeOutputParam> outputs);
~NodeGraph();
NodeInstance *get_node(const string &name);
NodeInstance *add_node(const string &type, const string &name = "");
const Input *get_input(int index) const;
const Input *get_input(const string &name) const;
const Output *get_output(int index) const;
const Output *get_output(const string &name) const;
void finalize();
/* first block is main */
const NodeBlock &main_block() const { return blocks.front(); }
protected:
const Input *add_input(const string &name, const string &type);
const Output *add_output(const string &name, const string &type, NodeConstant *default_value);
NodeInstance *add_proxy(const TypeDesc &typedesc, NodeConstant *default_value = NULL);
OutputKey add_value_node(NodeConstant *value);
OutputKey add_argument_node(const TypeDesc &typedesc);
void remove_all_nodes();
void insert_node(NodeInstance *node);
NodeInstance *copy_node(const NodeInstance *node);
NodeInstance *copy_node(const NodeInstance *node, NodeMap &node_map);
/* optimizations */
void remap_outputs(const OutputMap &replacements);
void ensure_valid_expression_inputs();
OutputKey find_root(const OutputKey &key);
void skip_pass_nodes();
#if 0
NodeInstance *inline_node(NodeInstance *old_node, const VariableMap &vars);
void inline_function_calls();
#endif
void remove_unused_nodes();
#if 0
bool add_block_node(NodeBlock &block, const OutputSet &local_vars,
NodeInstance *node, NodeSet &visited);
void blockify_nodes();
#endif
void sort_nodes();
public:
NodeInstanceMap nodes;
NodeBlockList blocks;
InputList inputs;
OutputList outputs;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:NodeGraph")
};
OpCode get_opcode_from_node_type(const string &node);
void nodes_init();
void nodes_free();
/* ========================================================================= */
/* inline functions */
template <typename T>
const NodeInput *NodeType::add_input(const string &name,
const string &type,
T default_value,
BVMInputValueType value_type)
{
NodeConstant *c = NodeConstant::create(TypeDesc(type), default_value);
BLI_assert(c != NULL);
return add_input(name, type, c, value_type);
}
} /* namespace blenvm */
#endif

View File

@@ -0,0 +1,36 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/node_value.cc
* \ingroup bvm
*/
#include "node_value.h"
namespace blenvm {
} /* namespace blenvm */

View File

@@ -0,0 +1,446 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/node_value.h
* \ingroup bvm
*/
#ifndef __BVM_VALUE_H__
#define __BVM_VALUE_H__
#include <vector>
#include "BVM_types.h"
#include "typedesc.h"
#include "util_math.h"
#include "util_string.h"
namespace blenvm {
template <BVMType type>
struct const_array {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
const_array(const POD *data, size_t size) :
m_data(data),
m_size(size)
{}
~const_array()
{}
const POD *data() const { return m_data; }
const POD& operator [] (size_t index) const
{
return m_data[index];
}
private:
POD *m_data;
size_t m_size;
};
template <BVMType type>
struct array {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
array() :
m_data(NULL),
m_size(0)
{}
array(POD *data, size_t size) :
m_data(data),
m_size(size)
{}
~array()
{}
operator const_array<type>() const
{
return const_array<type>(m_data, m_size);
}
POD *data() const { return m_data; }
POD& operator [] (size_t index)
{
return m_data[index];
}
private:
POD *m_data;
size_t m_size;
};
template <BVMType type>
struct const_image {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
const_image(const POD *data, size_t width, size_t height) :
m_data(data),
m_width(width),
m_height(height)
{}
~const_image()
{}
const POD *data() const { return m_data; }
const POD& get(size_t x, size_t y) const
{
return m_data[x + y * m_width];
}
private:
POD *m_data;
size_t m_width;
size_t m_height;
};
template <BVMType type>
struct image {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
image() :
m_data(NULL),
m_width(0),
m_height(0)
{}
image(const POD *data, size_t width, size_t height) :
m_data(data),
m_width(width),
m_height(height)
{}
~image()
{}
operator const_image<type>() const
{
return const_image<type>(m_data, m_width, m_height);
}
POD *data() const { return m_data; }
POD& get(size_t x, size_t y)
{
return m_data[x + y * m_width];
}
private:
POD *m_data;
size_t m_width;
size_t m_height;
};
/* ------------------------------------------------------------------------- */
struct NodeConstant {
template <typename T>
static NodeConstant *create(const TypeDesc &typedesc, T *data, size_t size);
template <typename T>
static NodeConstant *create(const TypeDesc &typedesc, T data);
virtual ~NodeConstant()
{}
const TypeDesc &typedesc() const { return m_typedesc; }
template <BVMType type>
bool get(array<type> *data) const;
template <typename T>
bool get(T *data) const;
virtual NodeConstant *copy() const = 0;
protected:
NodeConstant(const TypeDesc &typedesc) :
m_typedesc(typedesc)
{}
TypeDesc m_typedesc;
};
template <BVMType type>
struct SingleNodeValue : public NodeConstant {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
SingleNodeValue(const TypeDesc &td, typename traits::POD data) :
NodeConstant(td),
m_data(data)
{}
template <typename T>
SingleNodeValue(const TypeDesc &td, T data) :
NodeConstant(td)
{ (void)data; }
const POD &data() const { return m_data; }
bool get(POD *data) const
{
*data = m_data;
return true;
}
template <typename T>
bool get(T *data) const
{
assert(!"Data type mismatch");
(void)data;
return false;
}
NodeConstant *copy() const
{
return new SingleNodeValue<type>(m_typedesc, m_data);
}
private:
POD m_data;
};
template <BVMType type>
struct ArrayNodeValue : public NodeConstant {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
typedef array<type> array_t;
typedef const_array<type> const_array_t;
ArrayNodeValue(const TypeDesc &td, const array_t &data) :
NodeConstant(td),
m_data(data)
{}
ArrayNodeValue(const TypeDesc &td, POD *data, size_t size) :
NodeConstant(td),
m_data(array_t(data, size))
{}
template <typename T>
ArrayNodeValue(const TypeDesc &td, T data) :
NodeConstant(td)
{ (void)data; }
template <typename T>
ArrayNodeValue(const TypeDesc &td, T *data, size_t size) :
NodeConstant(td)
{ (void)data; (void)size; }
const array_t &data() const { return m_data; }
bool get(array_t *data) const
{
*data = m_data;
return true;
}
template <typename T>
bool get(T *data) const
{
assert(!"Data type mismatch");
(void)data;
return false;
}
NodeConstant *copy() const
{
return new ArrayNodeValue<type>(m_typedesc, m_data);
}
private:
array_t m_data;
};
template <BVMType type>
struct ImageNodeValue : public NodeConstant {
typedef BaseTypeTraits<type> traits;
typedef typename traits::POD POD;
typedef image<type> image_t;
typedef const_image<type> const_image_t;
ImageNodeValue(const image_t &data) :
NodeConstant(TypeSpec(type, BVM_BUFFER_ARRAY)),
m_data(data)
{}
ImageNodeValue(POD *data, size_t width, size_t height) :
NodeConstant(TypeDesc(type, BVM_BUFFER_ARRAY)),
m_data(image_t(data, width, height))
{}
template <typename T>
ImageNodeValue(T data) :
NodeConstant(TypeDesc(type, BVM_BUFFER_ARRAY))
{ (void)data; }
template <typename T>
ImageNodeValue(T *data, size_t width, size_t height) :
NodeConstant(TypeDesc(type, BVM_BUFFER_ARRAY))
{ (void)data; (void)width; (void)height; }
const image_t &data() const { return m_data; }
bool get(image_t *data) const
{
*data = m_data;
return true;
}
template <typename T>
bool get(T *data) const
{
assert(!"Data type mismatch");
(void)data;
return false;
}
NodeConstant *copy() const
{
return new ImageNodeValue<type>(m_data);
}
private:
image_t m_data;
};
/* ========================================================================= */
template <typename T>
static NodeConstant *create(const TypeDesc &td, T *data, size_t size)
{
const TypeSpec *typespec = td.get_typespec();
if (typespec->buffer_type() == BVM_BUFFER_ARRAY) {
switch (typespec->base_type()) {
case BVM_FLOAT: return new ArrayNodeValue<BVM_FLOAT>(data, size);
case BVM_FLOAT3: return new ArrayNodeValue<BVM_FLOAT3>(data, size);
case BVM_FLOAT4: return new ArrayNodeValue<BVM_FLOAT4>(data, size);
case BVM_INT: return new ArrayNodeValue<BVM_INT>(data, size);
case BVM_MATRIX44: return new ArrayNodeValue<BVM_MATRIX44>(data, size);
case BVM_STRING: return new ArrayNodeValue<BVM_STRING>(data, size);
case BVM_RNAPOINTER: return new ArrayNodeValue<BVM_RNAPOINTER>(data, size);
case BVM_MESH: return new ArrayNodeValue<BVM_MESH>(data, size);
case BVM_DUPLIS: return new ArrayNodeValue<BVM_DUPLIS>(data, size);
}
}
return NULL;
}
template <typename T>
NodeConstant *NodeConstant::create(const TypeDesc &td, T data)
{
const TypeSpec *typespec = td.get_typespec();
if (typespec->buffer_type() == BVM_BUFFER_SINGLE) {
switch (typespec->base_type()) {
case BVM_FLOAT: return new SingleNodeValue<BVM_FLOAT>(td, data);
case BVM_FLOAT3: return new SingleNodeValue<BVM_FLOAT3>(td, data);
case BVM_FLOAT4: return new SingleNodeValue<BVM_FLOAT4>(td, data);
case BVM_INT: return new SingleNodeValue<BVM_INT>(td, data);
case BVM_MATRIX44: return new SingleNodeValue<BVM_MATRIX44>(td, data);
case BVM_STRING: return new SingleNodeValue<BVM_STRING>(td, data);
case BVM_RNAPOINTER: return new SingleNodeValue<BVM_RNAPOINTER>(td, data);
case BVM_MESH: return new SingleNodeValue<BVM_MESH>(td, data);
case BVM_DUPLIS: return new SingleNodeValue<BVM_DUPLIS>(td, data);
}
}
else if (typespec->buffer_type() == BVM_BUFFER_ARRAY) {
switch (typespec->base_type()) {
case BVM_FLOAT: return new ArrayNodeValue<BVM_FLOAT>(td, data);
case BVM_FLOAT3: return new ArrayNodeValue<BVM_FLOAT3>(td, data);
case BVM_FLOAT4: return new ArrayNodeValue<BVM_FLOAT4>(td, data);
case BVM_INT: return new ArrayNodeValue<BVM_INT>(td, data);
case BVM_MATRIX44: return new ArrayNodeValue<BVM_MATRIX44>(td, data);
case BVM_STRING: return new ArrayNodeValue<BVM_STRING>(td, data);
case BVM_RNAPOINTER: return new ArrayNodeValue<BVM_RNAPOINTER>(td, data);
case BVM_MESH: return new ArrayNodeValue<BVM_MESH>(td, data);
case BVM_DUPLIS: return new ArrayNodeValue<BVM_DUPLIS>(td, data);
}
}
return NULL;
}
template <BVMType type>
bool NodeConstant::get(array<type> *data) const
{
const TypeSpec *typespec = m_typedesc.get_typespec();
if (typespec->buffer_type() == BVM_BUFFER_ARRAY) {
switch (typespec->base_type()) {
case BVM_FLOAT: return static_cast< const ArrayNodeValue<BVM_FLOAT>* >(this)->get(data);
case BVM_FLOAT3: return static_cast< const ArrayNodeValue<BVM_FLOAT3>* >(this)->get(data);
case BVM_FLOAT4: return static_cast< const ArrayNodeValue<BVM_FLOAT4>* >(this)->get(data);
case BVM_INT: return static_cast< const ArrayNodeValue<BVM_INT>* >(this)->get(data);
case BVM_MATRIX44: return static_cast< const ArrayNodeValue<BVM_MATRIX44>* >(this)->get(data);
case BVM_STRING: return static_cast< const ArrayNodeValue<BVM_STRING>* >(this)->get(data);
case BVM_RNAPOINTER: return static_cast< const ArrayNodeValue<BVM_RNAPOINTER>* >(this)->get(data);
case BVM_MESH: return static_cast< const ArrayNodeValue<BVM_MESH>* >(this)->get(data);
case BVM_DUPLIS: return static_cast< const ArrayNodeValue<BVM_DUPLIS>* >(this)->get(data);
}
}
return false;
}
template <typename T>
bool NodeConstant::get(T *data) const
{
const TypeSpec *typespec = m_typedesc.get_typespec();
if (typespec->buffer_type() == BVM_BUFFER_SINGLE) {
switch (typespec->base_type()) {
case BVM_FLOAT: return static_cast< const SingleNodeValue<BVM_FLOAT>* >(this)->get(data);
case BVM_FLOAT3: return static_cast< const SingleNodeValue<BVM_FLOAT3>* >(this)->get(data);
case BVM_FLOAT4: return static_cast< const SingleNodeValue<BVM_FLOAT4>* >(this)->get(data);
case BVM_INT: return static_cast< const SingleNodeValue<BVM_INT>* >(this)->get(data);
case BVM_MATRIX44: return static_cast< const SingleNodeValue<BVM_MATRIX44>* >(this)->get(data);
case BVM_STRING: return static_cast< const SingleNodeValue<BVM_STRING>* >(this)->get(data);
case BVM_RNAPOINTER: return static_cast< const SingleNodeValue<BVM_RNAPOINTER>* >(this)->get(data);
case BVM_MESH: return static_cast< const SingleNodeValue<BVM_MESH>* >(this)->get(data);
case BVM_DUPLIS: return static_cast< const SingleNodeValue<BVM_DUPLIS>* >(this)->get(data);
}
}
return false;
}
} /* namespace blenvm */
#endif

View File

@@ -0,0 +1,400 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/typedesc.cc
* \ingroup bvm
*/
#include "typedesc.h"
#include "node_value.h"
namespace blenvm {
StructSpec::StructSpec()
{
}
StructSpec::StructSpec(const StructSpec &other) :
m_fields(other.m_fields)
{
}
StructSpec::~StructSpec()
{
}
StructSpec& StructSpec::operator = (const StructSpec &other)
{
m_fields = other.m_fields;
return *this;
}
bool StructSpec::operator == (const StructSpec &other) const
{
/* vector comparison: checks equality of size and of each element */
return m_fields == other.m_fields;
}
int StructSpec::find_field(const string &name) const
{
for (int i = 0; i < m_fields.size(); ++i) {
if (m_fields[i].name == name)
return i;
}
return -1;
}
void StructSpec::add_field(const string &name, const TypeSpec *typespec)
{
m_fields.push_back(FieldSpec(name, typespec));
}
/* ------------------------------------------------------------------------- */
TypeSpec::TypeSpec(BVMType base_type, BVMBufferType buffer_type) :
m_base_type(base_type),
m_buffer_type(buffer_type),
m_structure(NULL)
{
}
TypeSpec::TypeSpec(const TypeSpec &other)
{
m_base_type = other.m_base_type;
m_buffer_type = other.m_buffer_type;
if (other.m_structure)
m_structure = new StructSpec(*other.m_structure);
else
m_structure = NULL;
}
TypeSpec::~TypeSpec()
{
if (m_structure)
delete m_structure;
}
TypeSpec& TypeSpec::operator = (const TypeSpec &other)
{
m_base_type = other.m_base_type;
m_buffer_type = other.m_buffer_type;
if (m_structure)
delete m_structure;
if (other.m_structure)
m_structure = new StructSpec(*other.m_structure);
else
m_structure = NULL;
return *this;
}
bool TypeSpec::operator == (const TypeSpec &other) const
{
if (is_structure() && other.is_structure()) {
return *m_structure == *other.m_structure;
}
else if (!is_structure() && !other.is_structure()) {
return m_base_type == other.m_base_type &&
m_buffer_type == other.m_buffer_type;
}
else
return false;
}
bool TypeSpec::operator < (const TypeSpec &other) const
{
const StructSpec *sa = m_structure;
const StructSpec *sb = other.m_structure;
if (!sa && !sb) {
/* neither type is a struct, compare base types */
if (m_base_type == other.m_base_type) {
return m_buffer_type < other.m_buffer_type;
}
else {
return m_base_type < other.m_base_type;
}
}
else if (sa && sb) {
/* both are structs, make deep comparison */
if (sa->num_fields() == sb->num_fields()) {
int num_fields = sa->num_fields();
for (int i = 0; i < num_fields; ++i) {
const StructSpec::FieldSpec &fa = sa->field(i);
const StructSpec::FieldSpec &fb = sb->field(i);
if (fa.typespec < fb.typespec) {
return true;
}
else if (fb.typespec < fa.typespec) {
return false;
}
else {
continue;
}
}
/* if we get here: all fields are equivalent */
return false;
}
else {
return sa->num_fields() < sb->num_fields();
}
}
else if (!sa && sb) {
return true;
}
else { /* sa && !sb */
return false;
}
}
bool TypeSpec::is_aggregate() const
{
switch (m_base_type) {
case BVM_FLOAT:
case BVM_INT:
return false;
case BVM_FLOAT3:
case BVM_FLOAT4:
case BVM_MATRIX44:
case BVM_STRING:
case BVM_RNAPOINTER:
case BVM_MESH:
case BVM_DUPLIS:
return true;
}
return false;
}
bool TypeSpec::assignable(const TypeSpec &other) const
{
return *this == other;
}
size_t TypeSpec::size() const
{
if (m_structure) {
size_t size = 0;
for (int i = 0; i < m_structure->num_fields(); ++i)
size += m_structure->field(i).typespec->size();
return size;
}
else {
switch (m_buffer_type) {
case BVM_BUFFER_SINGLE:
switch (m_base_type) {
case BVM_FLOAT: return BaseTypeTraits<BVM_FLOAT>::size;
case BVM_FLOAT3: return BaseTypeTraits<BVM_FLOAT3>::size;
case BVM_FLOAT4: return BaseTypeTraits<BVM_FLOAT4>::size;
case BVM_INT: return BaseTypeTraits<BVM_INT>::size;
case BVM_MATRIX44: return BaseTypeTraits<BVM_MATRIX44>::size;
case BVM_STRING: return BaseTypeTraits<BVM_STRING>::size;
case BVM_RNAPOINTER: return BaseTypeTraits<BVM_RNAPOINTER>::size;
case BVM_MESH: return BaseTypeTraits<BVM_MESH>::size;
case BVM_DUPLIS: return BaseTypeTraits<BVM_DUPLIS>::size;
}
break;
case BVM_BUFFER_ARRAY:
switch (m_base_type) {
case BVM_FLOAT: return sizeof(array<BVM_FLOAT>);
case BVM_FLOAT3: return sizeof(array<BVM_FLOAT>);
case BVM_FLOAT4: return sizeof(array<BVM_FLOAT>);
case BVM_INT: return sizeof(array<BVM_FLOAT>);
case BVM_MATRIX44: return sizeof(array<BVM_FLOAT>);
case BVM_STRING: return sizeof(array<BVM_FLOAT>);
case BVM_RNAPOINTER: return sizeof(array<BVM_FLOAT>);
case BVM_MESH: return sizeof(array<BVM_FLOAT>);
case BVM_DUPLIS: return sizeof(array<BVM_FLOAT>);
}
case BVM_BUFFER_IMAGE:
switch (m_base_type) {
case BVM_FLOAT: return sizeof(image<BVM_FLOAT>);
case BVM_FLOAT3: return sizeof(image<BVM_FLOAT>);
case BVM_FLOAT4: return sizeof(image<BVM_FLOAT>);
case BVM_INT: return sizeof(image<BVM_FLOAT>);
case BVM_MATRIX44: return sizeof(image<BVM_FLOAT>);
case BVM_STRING: return sizeof(image<BVM_FLOAT>);
case BVM_RNAPOINTER: return sizeof(image<BVM_FLOAT>);
case BVM_MESH: return sizeof(image<BVM_FLOAT>);
case BVM_DUPLIS: return sizeof(image<BVM_FLOAT>);
}
}
}
return 0;
}
void TypeSpec::copy_value(void *to, const void *from) const
{
if (m_structure) {
for (int i = 0; i < m_structure->num_fields(); ++i) {
m_structure->field(i).typespec->copy_value(to, from);
size_t size = m_structure->field(i).typespec->size();
to = ((uint8_t *)to) + size;
from = ((uint8_t *)from) + size;
}
}
else {
switch (m_buffer_type) {
case BVM_BUFFER_SINGLE:
#define COPY_TYPE(a, b, type) \
BaseTypeTraits<type>::copy((BaseTypeTraits<type>::POD*)(a), (BaseTypeTraits<type>::POD const *)(b));
switch (m_base_type) {
case BVM_FLOAT: COPY_TYPE(to, from, BVM_FLOAT); break;
case BVM_FLOAT3: COPY_TYPE(to, from, BVM_FLOAT3); break;
case BVM_FLOAT4: COPY_TYPE(to, from, BVM_FLOAT4); break;
case BVM_INT: COPY_TYPE(to, from, BVM_INT); break;
case BVM_MATRIX44: COPY_TYPE(to, from, BVM_MATRIX44); break;
case BVM_STRING: COPY_TYPE(to, from, BVM_STRING); break;
case BVM_RNAPOINTER: COPY_TYPE(to, from, BVM_RNAPOINTER); break;
case BVM_MESH: COPY_TYPE(to, from, BVM_MESH); break;
case BVM_DUPLIS: COPY_TYPE(to, from, BVM_DUPLIS); break;
}
#undef COPY_TYPE
break;
case BVM_BUFFER_ARRAY:
#define COPY_TYPE(a, b, type) \
*(array<type> *)(a) = *(array<type> const *)(b);
switch (m_base_type) {
case BVM_FLOAT: COPY_TYPE(to, from, BVM_FLOAT); break;
case BVM_FLOAT3: COPY_TYPE(to, from, BVM_FLOAT3); break;
case BVM_FLOAT4: COPY_TYPE(to, from, BVM_FLOAT4); break;
case BVM_INT: COPY_TYPE(to, from, BVM_INT); break;
case BVM_MATRIX44: COPY_TYPE(to, from, BVM_MATRIX44); break;
case BVM_STRING: COPY_TYPE(to, from, BVM_STRING); break;
case BVM_RNAPOINTER: COPY_TYPE(to, from, BVM_RNAPOINTER); break;
case BVM_MESH: COPY_TYPE(to, from, BVM_MESH); break;
case BVM_DUPLIS: COPY_TYPE(to, from, BVM_DUPLIS); break;
}
#undef COPY_TYPE
break;
case BVM_BUFFER_IMAGE:
#define COPY_TYPE(a, b, type) \
*(image<type> *)(a) = *(image<type> const *)(b);
switch (m_base_type) {
case BVM_FLOAT: COPY_TYPE(to, from, BVM_FLOAT); break;
case BVM_FLOAT3: COPY_TYPE(to, from, BVM_FLOAT3); break;
case BVM_FLOAT4: COPY_TYPE(to, from, BVM_FLOAT4); break;
case BVM_INT: COPY_TYPE(to, from, BVM_INT); break;
case BVM_MATRIX44: COPY_TYPE(to, from, BVM_MATRIX44); break;
case BVM_STRING: COPY_TYPE(to, from, BVM_STRING); break;
case BVM_RNAPOINTER: COPY_TYPE(to, from, BVM_RNAPOINTER); break;
case BVM_MESH: COPY_TYPE(to, from, BVM_MESH); break;
case BVM_DUPLIS: COPY_TYPE(to, from, BVM_DUPLIS); break;
}
#undef COPY_TYPE
break;
}
}
}
StructSpec* TypeSpec::make_structure()
{
assert(m_structure == NULL);
m_structure = new StructSpec();
return m_structure;
}
TypeSpec::TypeDefMap TypeSpec::m_typedefs;
const TypeSpec* TypeSpec::get_typespec(const string &name)
{
TypeDefMap::const_iterator it = m_typedefs.find(name);
if (it != m_typedefs.end())
return it->second;
else
return NULL;
}
TypeSpec *TypeSpec::add_typespec(const string &name, BVMType base_type, BVMBufferType buffer_type)
{
BLI_assert (m_typedefs.find(name) == m_typedefs.end());
TypeSpec *ts = new TypeSpec(base_type, buffer_type);
m_typedefs.insert(TypeDefMap::value_type(name, ts));
return ts;
}
void TypeSpec::remove_typespec(const string &name)
{
TypeDefMap::iterator it = m_typedefs.find(name);
if (it != m_typedefs.end()) {
delete it->second;
m_typedefs.erase(it);
}
}
void TypeSpec::clear_typespecs()
{
for (TypeDefMap::iterator it = m_typedefs.begin(); it != m_typedefs.end(); ++it) {
delete it->second;
}
m_typedefs.clear();
}
TypeSpec::typedef_iterator TypeSpec::typespec_begin()
{
return m_typedefs.begin();
}
TypeSpec::typedef_iterator TypeSpec::typespec_end()
{
return m_typedefs.end();
}
/* ------------------------------------------------------------------------- */
TypeDesc::TypeDesc(const string &name) :
m_name(name)
{
}
TypeDesc::TypeDesc(const TypeDesc &other)
{
m_name = other.m_name;
}
TypeDesc::~TypeDesc()
{
}
bool TypeDesc::has_typespec() const
{
const TypeSpec *ts = TypeSpec::get_typespec(m_name);
return ts != NULL;
}
const TypeSpec *TypeDesc::get_typespec() const
{
const TypeSpec *ts = TypeSpec::get_typespec(m_name);
BLI_assert(ts != NULL);
return ts;
}
} /* namespace blenvm */

View File

@@ -0,0 +1,261 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/typedesc.h
* \ingroup bvm
*/
#ifndef __BVM_TYPEDESC_H__
#define __BVM_TYPEDESC_H__
#include <cassert>
#include <map>
#include <vector>
extern "C" {
#include "RNA_access.h"
}
#include "BVM_types.h"
#include "util_data_ptr.h"
#include "util_math.h"
#include "util_string.h"
namespace blenvm {
struct NodeGraph;
struct NodeConstant;
template <BVMType type>
struct BaseTypeTraits;
template <>
struct BaseTypeTraits<BVM_FLOAT> {
typedef float POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_FLOAT3> {
typedef float3 POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_FLOAT4> {
typedef float4 POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_INT> {
typedef int POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_MATRIX44> {
typedef matrix44 POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_STRING> {
typedef const char* POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_RNAPOINTER> {
typedef PointerRNA POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_MESH> {
typedef mesh_ptr POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
template <>
struct BaseTypeTraits<BVM_DUPLIS> {
typedef duplis_ptr POD;
enum eStackSize { size = sizeof(POD) };
static inline void copy(POD *to, const POD *from)
{
*to = *from;
}
};
/* ------------------------------------------------------------------------- */
struct StructSpec;
struct TypeSpec {
typedef std::map<string, const TypeSpec *> TypeDefMap;
typedef TypeDefMap::const_iterator typedef_iterator;
TypeSpec(BVMType base_type, BVMBufferType buffer_type = BVM_BUFFER_SINGLE);
TypeSpec(const TypeSpec &other);
~TypeSpec();
TypeSpec& operator = (const TypeSpec &other);
bool operator == (const TypeSpec &other) const;
bool operator < (const TypeSpec &other) const;
BVMType base_type() const { return m_base_type; }
BVMBufferType buffer_type() const { return m_buffer_type; }
bool is_aggregate() const;
bool assignable(const TypeSpec &other) const;
size_t size() const;
void copy_value(void *to, const void *from) const;
bool is_structure() const { return m_structure != NULL; }
const StructSpec *structure() const { return m_structure; }
StructSpec *structure() { return m_structure; }
StructSpec *make_structure();
static const TypeSpec* get_typespec(const string &name);
static TypeSpec *add_typespec(const string &name, BVMType base_type, BVMBufferType buffer_type = BVM_BUFFER_SINGLE);
static void remove_typespec(const string &name);
static void clear_typespecs();
static typedef_iterator typespec_begin();
static typedef_iterator typespec_end();
private:
BVMType m_base_type;
BVMBufferType m_buffer_type;
StructSpec *m_structure;
static TypeDefMap m_typedefs;
};
struct StructSpec {
struct FieldSpec {
FieldSpec(const string &name, const TypeSpec *typespec) :
name(name),
typespec(typespec)
{}
bool operator == (const FieldSpec &other) const
{
return name == other.name && typespec == other.typespec;
}
string name;
const TypeSpec *typespec;
};
typedef std::vector<FieldSpec> FieldList;
StructSpec();
StructSpec(const StructSpec &other);
~StructSpec();
StructSpec& operator = (const StructSpec &other);
bool operator == (const StructSpec &other) const;
int num_fields() const { return m_fields.size(); }
const FieldSpec &field(int i) const { return m_fields[i]; }
int find_field(const string &name) const;
void add_field(const string &name, const TypeSpec *typespec);
private:
FieldList m_fields;
};
/* ------------------------------------------------------------------------- */
struct TypeDesc {
TypeDesc(const string &name);
TypeDesc(const TypeDesc &other);
~TypeDesc();
const string &name() const { return m_name; }
bool has_typespec() const;
const TypeSpec *get_typespec() const;
private:
string m_name;
};
} /* namespace blenvm */
#endif

View File

@@ -0,0 +1,885 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/bvm_api.cc
* \ingroup bvm
*/
#include <set>
#include <string.h>
#include "MEM_guardedalloc.h"
extern "C" {
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "BKE_anim.h"
#include "BKE_effect.h"
#include "BKE_node.h"
#include "DEG_depsgraph_build.h"
#include "RE_shader_ext.h"
#include "BVM_api.h"
#include "RNA_access.h"
}
#include "function_cache.h"
#include "node_graph.h"
#include "typedesc.h"
#include "bvm_codegen.h"
#include "bvm_eval.h"
#include "bvm_function.h"
#ifdef WITH_LLVM
#include "llvm_codegen.h"
#include "llvm_engine.h"
#include "llvm_function.h"
#endif
#include "util_array.h"
#include "util_debug.h"
#include "util_map.h"
#include "util_thread.h"
#include "util_string.h"
namespace blenvm {
static mesh_ptr __empty_mesh__;
static duplis_ptr __empty_duplilist__ = duplis_ptr(new DupliList());
static blenvm::EvalGlobals *eval_globals_default()
{
static blenvm::EvalGlobals default_globals;
return &default_globals;
}
static std::vector<NodeInputParam> forcefield_inputs;
static std::vector<NodeOutputParam> forcefield_outputs;
static std::vector<NodeInputParam> texture_inputs;
static std::vector<NodeOutputParam> texture_outputs;
static std::vector<NodeInputParam> modifier_inputs;
static std::vector<NodeOutputParam> modifier_outputs;
static std::vector<NodeInputParam> dupli_inputs;
static std::vector<NodeOutputParam> dupli_outputs;
static void register_graph_types()
{
static const float3 zerovec(0.0f, 0.0f, 0.0f);
static const float C[4] = {0.0f, 0.0f, 0.0f, 1.0f};
static const float N[3] = {0.0f, 0.0f, 0.0f};
forcefield_inputs.push_back(NodeInputParam("effector.object", "RNAPOINTER"));
forcefield_inputs.push_back(NodeInputParam("effector.position", "FLOAT3"));
forcefield_inputs.push_back(NodeInputParam("effector.velocity", "FLOAT3"));
forcefield_outputs.push_back(NodeOutputParam("force", "FLOAT3", zerovec));
forcefield_outputs.push_back(NodeOutputParam("impulse", "FLOAT3", zerovec));
texture_inputs.push_back(NodeInputParam("texture.co", "FLOAT3"));
texture_inputs.push_back(NodeInputParam("texture.cfra", "INT"));
texture_inputs.push_back(NodeInputParam("texture.osatex", "INT"));
texture_outputs.push_back(NodeOutputParam("color", "FLOAT4", C));
texture_outputs.push_back(NodeOutputParam("normal", "FLOAT3", N));
modifier_inputs.push_back(NodeInputParam("modifier.object", "RNAPOINTER"));
modifier_inputs.push_back(NodeInputParam("modifier.base_mesh", "RNAPOINTER"));
modifier_outputs.push_back(NodeOutputParam("mesh", "MESH", __empty_mesh__));
dupli_inputs.push_back(NodeInputParam("dupli.object", "RNAPOINTER"));
dupli_outputs.push_back(NodeOutputParam("dupli.result", "DUPLIS", __empty_duplilist__));
}
}
void BVM_init(void)
{
using namespace blenvm;
create_empty_mesh(__empty_mesh__);
nodes_init();
register_graph_types();
#ifdef WITH_LLVM
llvm_init();
#endif
}
void BVM_free(void)
{
using namespace blenvm;
blenvm::function_bvm_cache_clear();
#ifdef WITH_LLVM
blenvm::function_llvm_cache_clear();
llvm_free();
#endif
nodes_free();
TypeSpec::clear_typespecs();
destroy_empty_mesh(__empty_mesh__);
}
/* ------------------------------------------------------------------------- */
BLI_INLINE blenvm::NodeGraph *_GRAPH(struct BVMNodeGraph *graph)
{ return (blenvm::NodeGraph *)graph; }
BLI_INLINE blenvm::NodeInstance *_NODE(struct BVMNodeInstance *node)
{ return (blenvm::NodeInstance *)node; }
BLI_INLINE blenvm::NodeInput *_INPUT(struct BVMNodeInput *input)
{ return (blenvm::NodeInput *)input; }
BLI_INLINE blenvm::NodeOutput *_OUTPUT(struct BVMNodeOutput *output)
{ return (blenvm::NodeOutput *)output; }
BLI_INLINE blenvm::TypeDesc *_TYPEDESC(struct BVMTypeDesc *typedesc)
{ return (blenvm::TypeDesc *)typedesc; }
struct BVMNodeInstance *BVM_nodegraph_add_node(BVMNodeGraph *graph, const char *type, const char *name)
{ return (struct BVMNodeInstance *)_GRAPH(graph)->add_node(type, name); }
void BVM_nodegraph_get_input(struct BVMNodeGraph *graph, const char *name,
struct BVMNodeInstance **node, const char **socket)
{
const blenvm::NodeGraph::Input *input = _GRAPH(graph)->get_input(name);
if (input) {
if (node) *node = (BVMNodeInstance *)input->key.node;
if (socket) *socket = input->key.socket->name.c_str();
}
else {
if (node) *node = NULL;
if (socket) *socket = "";
}
}
void BVM_nodegraph_get_output(struct BVMNodeGraph *graph, const char *name,
struct BVMNodeInstance **node, const char **socket)
{
const blenvm::NodeGraph::Output *output = _GRAPH(graph)->get_output(name);
if (output) {
if (node) *node = (BVMNodeInstance *)output->key.node;
if (socket) *socket = output->key.socket->name.c_str();
}
else {
if (node) *node = NULL;
if (socket) *socket = "";
}
}
int BVM_node_num_inputs(struct BVMNodeInstance *node)
{ return _NODE(node)->num_inputs(); }
int BVM_node_num_outputs(struct BVMNodeInstance *node)
{ return _NODE(node)->num_outputs(); }
struct BVMNodeInput *BVM_node_get_input(struct BVMNodeInstance *node, const char *name)
{ return (struct BVMNodeInput *)_NODE(node)->type->find_input(name); }
struct BVMNodeInput *BVM_node_get_input_n(struct BVMNodeInstance *node, int index)
{
if (index >= 0 && index < _NODE(node)->num_inputs())
return (struct BVMNodeInput *)_NODE(node)->type->find_input(index);
else
return NULL;
}
bool BVM_node_set_input_link(struct BVMNodeInstance *node, struct BVMNodeInput *input,
struct BVMNodeInstance *from_node, struct BVMNodeOutput *from_output)
{
return _NODE(node)->link_set(_INPUT(input)->name, blenvm::OutputKey(_NODE(from_node), _OUTPUT(from_output)->name));
}
struct BVMNodeOutput *BVM_node_get_output(struct BVMNodeInstance *node, const char *name)
{ return (struct BVMNodeOutput *)_NODE(node)->type->find_output(name); }
struct BVMNodeOutput *BVM_node_get_output_n(struct BVMNodeInstance *node, int index)
{
if (index >= 0 && index < _NODE(node)->num_outputs())
return (struct BVMNodeOutput *)_NODE(node)->type->find_output(index);
else
return NULL;
}
void BVM_node_set_input_value_float(struct BVMNodeInstance *node, struct BVMNodeInput *input,
float value)
{ _NODE(node)->input_value_set(_INPUT(input)->name, value); }
void BVM_node_set_input_value_float3(struct BVMNodeInstance *node, struct BVMNodeInput *input,
const float value[3])
{ _NODE(node)->input_value_set(_INPUT(input)->name, blenvm::float3::from_data(value)); }
void BVM_node_set_input_value_float4(struct BVMNodeInstance *node, struct BVMNodeInput *input,
const float value[4])
{ _NODE(node)->input_value_set(_INPUT(input)->name, blenvm::float4::from_data(value)); }
void BVM_node_set_input_value_matrix44(struct BVMNodeInstance *node, struct BVMNodeInput *input,
float value[4][4])
{ _NODE(node)->input_value_set(_INPUT(input)->name, blenvm::matrix44::from_data(&value[0][0])); }
void BVM_node_set_input_value_int(struct BVMNodeInstance *node, struct BVMNodeInput *input,
int value)
{ _NODE(node)->input_value_set(_INPUT(input)->name, value); }
const char *BVM_node_input_name(struct BVMNodeInput *input)
{ return _INPUT(input)->name.c_str(); }
struct BVMTypeDesc *BVM_node_input_typedesc(struct BVMNodeInput *input)
{ return (struct BVMTypeDesc *)(&_INPUT(input)->typedesc); }
BVMInputValueType BVM_node_input_value_type(struct BVMNodeInput *input)
{ return _INPUT(input)->value_type; }
const char *BVM_node_output_name(struct BVMNodeOutput *output)
{ return _OUTPUT(output)->name.c_str(); }
struct BVMTypeDesc *BVM_node_output_typedesc(struct BVMNodeOutput *output)
{ return (struct BVMTypeDesc *)(&_OUTPUT(output)->typedesc); }
BVMOutputValueType BVM_node_output_value_type(struct BVMNodeOutput *output)
{ return _OUTPUT(output)->value_type; }
BVMType BVM_typedesc_base_type(struct BVMTypeDesc *typedesc)
{ return _TYPEDESC(typedesc)->get_typespec()->base_type(); }
BVMBufferType BVM_typedesc_buffer_type(struct BVMTypeDesc *typedesc)
{ return _TYPEDESC(typedesc)->get_typespec()->buffer_type(); }
/* ------------------------------------------------------------------------- */
void BVM_nodetree_compile_dependencies(bNodeTree *ntree, DepsNodeHandle *handle)
{
PointerRNA ptr;
ParameterList list;
FunctionRNA *func;
if (!ntree->typeinfo->ext.call)
return;
RNA_id_pointer_create((ID *)ntree, &ptr);
func = RNA_struct_find_function(ptr.type, "bvm_compile_dependencies");
if (!func)
return;
RNA_parameter_list_create(&list, &ptr, func);
RNA_parameter_set_lookup(&list, "depsnode", &handle);
ntree->typeinfo->ext.call(NULL, &ptr, func, &list);
RNA_parameter_list_free(&list);
}
void BVM_nodetree_eval_dependencies(bNodeTree *ntree, DepsNodeHandle *handle)
{
PointerRNA ptr;
ParameterList list;
FunctionRNA *func;
if (!ntree->typeinfo->ext.call)
return;
RNA_id_pointer_create((ID *)ntree, &ptr);
func = RNA_struct_find_function(ptr.type, "bvm_eval_dependencies");
if (!func)
return;
RNA_parameter_list_create(&list, &ptr, func);
RNA_parameter_set_lookup(&list, "depsnode", &handle);
ntree->typeinfo->ext.call(NULL, &ptr, func, &list);
RNA_parameter_list_free(&list);
}
/* ------------------------------------------------------------------------- */
BLI_INLINE blenvm::EvalGlobals *_GLOBALS(struct BVMEvalGlobals *globals)
{ return (blenvm::EvalGlobals *)globals; }
BLI_INLINE const blenvm::EvalGlobals *_GLOBALS(const struct BVMEvalGlobals *globals)
{ return (const blenvm::EvalGlobals *)globals; }
BLI_INLINE blenvm::EvalContext *_CTX(struct BVMEvalContext *ctx)
{ return (blenvm::EvalContext *)ctx; }
struct BVMEvalGlobals *BVM_globals_create(void)
{ return (BVMEvalGlobals *)(new blenvm::EvalGlobals()); }
void BVM_globals_free(struct BVMEvalGlobals *globals)
{ delete _GLOBALS(globals); }
struct ImagePool *BVM_globals_image_pool(struct BVMEvalGlobals *globals)
{ return _GLOBALS(globals)->image_pool(); }
void BVM_globals_add_object(struct BVMEvalGlobals *globals, int key, struct Object *ob)
{ _GLOBALS(globals)->add_object(key, ob); }
namespace blenvm {
struct EvalGlobalsHandle
{
static void add_object_relation(DepsNodeHandle *_handle, struct Object *ob, eDepsComponent /*component*/, const char */*description*/)
{
EvalGlobalsHandle *handle = (EvalGlobalsHandle *)_handle;
handle->globals->add_object(EvalGlobals::get_id_key((ID *)ob), ob);
}
static void add_bone_relation(DepsNodeHandle *_handle, struct Object *ob, const char */*bone_name*/, eDepsComponent /*component*/, const char */*description*/)
{
EvalGlobalsHandle *handle = (EvalGlobalsHandle *)_handle;
handle->globals->add_object(EvalGlobals::get_id_key((ID *)ob), ob);
}
static void add_image_relation(DepsNodeHandle *_handle, struct Image *ima, eDepsComponent /*component*/, const char */*description*/)
{
EvalGlobalsHandle *handle = (EvalGlobalsHandle *)_handle;
handle->globals->add_image(EvalGlobals::get_id_key((ID *)ima), ima);
}
EvalGlobalsHandle(EvalGlobals *globals) :
globals(globals)
{
memset(&handle, 0, sizeof(handle));
handle.add_object_relation = add_object_relation;
handle.add_bone_relation = add_bone_relation;
handle.add_image_relation = add_image_relation;
}
DepsNodeHandle handle;
EvalGlobals *globals;
};
static void rna_globals_update(bNodeTree *ntree, blenvm::EvalGlobals *globals)
{
EvalGlobalsHandle handle(globals);
DepsNodeHandle *phandle = &handle.handle;
PointerRNA ptr;
ParameterList list;
FunctionRNA *func;
if (!ntree->typeinfo->ext.call)
return;
RNA_id_pointer_create((ID *)ntree, &ptr);
func = RNA_struct_find_function(ptr.type, "bvm_eval_dependencies");
if (!func)
return;
RNA_parameter_list_create(&list, &ptr, func);
RNA_parameter_set_lookup(&list, "depsnode", &phandle);
ntree->typeinfo->ext.call(NULL, &ptr, func, &list);
RNA_parameter_list_free(&list);
}
} /* namespace blenvm */
void BVM_globals_add_nodetree_relations(struct BVMEvalGlobals *globals, bNodeTree *ntree)
{ rna_globals_update(ntree, _GLOBALS(globals)); }
int BVM_get_id_key(struct ID *id)
{ return blenvm::EvalGlobals::get_id_key(id); }
struct BVMEvalContext *BVM_context_create(void)
{ return (BVMEvalContext *)(new blenvm::EvalContext()); }
void BVM_context_free(struct BVMEvalContext *ctx)
{ delete _CTX(ctx); }
/* ------------------------------------------------------------------------- */
BLI_INLINE blenvm::FunctionBVM *_FUNC_BVM(struct BVMFunction *fn)
{ return (blenvm::FunctionBVM *)fn; }
static blenvm::spin_lock bvm_lock = blenvm::spin_lock();
void BVM_function_bvm_release(BVMFunction *fn)
{
bvm_lock.lock();
blenvm::function_bvm_cache_release(_FUNC_BVM(fn));
bvm_lock.unlock();
}
void BVM_function_bvm_cache_remove(void *key)
{
bvm_lock.lock();
blenvm::function_bvm_cache_remove(key);
#ifdef WITH_LLVM
blenvm::function_llvm_cache_remove(key);
#endif
bvm_lock.unlock();
}
#ifdef WITH_LLVM
BLI_INLINE blenvm::FunctionLLVM *_FUNC_LLVM(struct BVMFunction *fn)
{ return (blenvm::FunctionLLVM *)fn; }
static blenvm::spin_lock llvm_lock = blenvm::spin_lock();
void BVM_function_llvm_release(BVMFunction *fn)
{
llvm_lock.lock();
blenvm::function_llvm_cache_release(_FUNC_LLVM(fn));
llvm_lock.unlock();
}
void BVM_function_llvm_cache_remove(void *key)
{
llvm_lock.lock();
blenvm::function_llvm_cache_remove(key);
llvm_lock.unlock();
}
#else
void BVM_function_llvm_release(BVMFunction */*fn*/) {}
void BVM_function_llvm_cache_remove(void */*key*/) {}
#endif
/* ------------------------------------------------------------------------- */
namespace blenvm {
static string get_ntree_unique_function_name(bNodeTree *ntree)
{
std::stringstream ss;
ss << "nodetree_" << ntree;
// ss << ntree->id.name << "_" << ntree;
return ss.str();
}
static void parse_py_nodes(bNodeTree *btree, NodeGraph *graph)
{
PointerRNA ptr;
ParameterList list;
FunctionRNA *func;
RNA_id_pointer_create((ID *)btree, &ptr);
func = RNA_struct_find_function(ptr.type, "bvm_compile");
if (!func)
return;
RNA_parameter_list_create(&list, &ptr, func);
RNA_parameter_set_lookup(&list, "graph", &graph);
btree->typeinfo->ext.call(NULL, &ptr, func, &list);
RNA_parameter_list_free(&list);
}
static void debug_node_graph(blenvm::NodeGraph &graph, FILE *debug_file, const char *label, BVMDebugMode mode)
{
if (mode != BVM_DEBUG_NODES_UNOPTIMIZED)
graph.finalize();
switch (mode) {
case BVM_DEBUG_NODES:
case BVM_DEBUG_NODES_UNOPTIMIZED: {
debug::NodeGraphDumper dumper(debug_file);
dumper.dump_graph(&graph, label);
break;
}
case BVM_DEBUG_BVM_CODE: {
DebugGraphvizBVMCompiler compiler;
compiler.compile_function(graph, debug_file, label);
break;
}
case BVM_DEBUG_LLVM_CODE: {
#ifdef WITH_LLVM
LLVMCodeGenerator codegen(2);
Compiler compiler(&codegen);
compiler.debug_node_graph(label, graph, debug_file);
#endif
break;
}
case BVM_DEBUG_LLVM_CODE_UNOPTIMIZED: {
#ifdef WITH_LLVM
LLVMCodeGenerator codegen(0);
Compiler compiler(&codegen);
compiler.debug_node_graph(label, graph, debug_file);
#endif
break;
}
}
}
static struct BVMFunction *gen_function_bvm(struct bNodeTree *btree, bool use_cache,
ArrayRef<NodeInputParam> inputs,
ArrayRef<NodeOutputParam> outputs)
{
using namespace blenvm;
bvm_lock.lock();
FunctionBVM *fn = NULL;
if (use_cache) {
fn = function_bvm_cache_acquire(btree);
}
if (!fn) {
NodeGraph graph(inputs, outputs);
parse_py_nodes(btree, &graph);
graph.finalize();
BVMCompiler compiler;
fn = compiler.compile_function(graph);
if (use_cache) {
function_bvm_cache_set(btree, fn);
}
}
fn->retain(fn);
bvm_lock.unlock();
return (BVMFunction *)fn;
}
static struct BVMFunction *gen_function_llvm(struct bNodeTree *btree, bool use_cache,
ArrayRef<NodeInputParam> inputs,
ArrayRef<NodeOutputParam> outputs)
{
#ifdef WITH_LLVM
using namespace blenvm;
llvm_lock.lock();
FunctionLLVM *fn = NULL;
if (use_cache) {
fn = function_llvm_cache_acquire(btree);
}
if (!fn) {
NodeGraph graph(inputs, outputs);
parse_py_nodes(btree, &graph);
graph.finalize();
LLVMCodeGenerator codegen(2);
Compiler compiler(&codegen);
compiler.compile_node_graph(get_ntree_unique_function_name(btree), graph);
fn = new FunctionLLVM(codegen.function_address());
if (use_cache) {
function_llvm_cache_set(btree, fn);
}
}
fn->retain(fn);
llvm_lock.unlock();
return (BVMFunction *)fn;
#else
UNUSED_VARS(btree, use_cache);
return NULL;
#endif
}
static void debug_nodes(bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode,
ArrayRef<NodeInputParam> inputs,
ArrayRef<NodeOutputParam> outputs)
{
using namespace blenvm;
NodeGraph graph(inputs, outputs);
parse_py_nodes(btree, &graph);
debug_node_graph(graph, debug_file, label, mode);
}
} /* namespace blenvm */
/* ========================================================================= */
struct BVMFunction *BVM_gen_forcefield_function_bvm(bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_bvm(btree, use_cache, forcefield_inputs, forcefield_outputs);
}
void BVM_debug_forcefield_nodes(bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode)
{
using namespace blenvm;
debug_nodes(btree, debug_file, label, mode, forcefield_inputs, forcefield_outputs);
}
void BVM_eval_forcefield_bvm(struct BVMEvalGlobals *globals, struct BVMEvalContext *ctx, struct BVMFunction *fn,
struct Object *effob, const EffectedPoint *point,
float force[3], float impulse[3])
{
using namespace blenvm;
PointerRNA object_ptr;
RNA_id_pointer_create((ID *)effob, &object_ptr);
const void *args[] = { &object_ptr, point->loc, point->vel };
void *results[] = { force, impulse };
_FUNC_BVM(fn)->eval(_CTX(ctx), _GLOBALS(globals), args, results);
}
/* ------------------------------------------------------------------------- */
namespace blenvm {
typedef void (*TexNodesFunc)(const struct EvalGlobals *globals,
Dual2<float4> *r_color, Dual2<float3> *r_normal,
const Dual2<float3> *co, int cfra, int osatex);
static void set_texresult(TexResult *result, const float4 &color, const float3 &normal)
{
result->tr = color.x;
result->tg = color.y;
result->tb = color.z;
result->ta = color.w;
result->tin = (result->tr + result->tg + result->tb) / 3.0f;
result->talpha = true;
if (result->nor) {
result->nor[0] = normal.x;
result->nor[1] = normal.y;
result->nor[2] = normal.z;
}
}
}
struct BVMFunction *BVM_gen_texture_function_bvm(bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_bvm(btree, use_cache, texture_inputs, texture_outputs);
}
struct BVMFunction *BVM_gen_texture_function_llvm(bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_llvm(btree, use_cache, texture_inputs, texture_outputs);
}
void BVM_debug_texture_nodes(bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode)
{
using namespace blenvm;
debug_nodes(btree, debug_file, label, mode, texture_inputs, texture_outputs);
}
void BVM_eval_texture_bvm(struct BVMEvalGlobals *_globals, struct BVMEvalContext *ctx, struct BVMFunction *fn,
struct TexResult *target,
float coord[3], float dxt[3], float dyt[3], int osatex,
short UNUSED(which_output), int cfra, int UNUSED(preview))
{
using namespace blenvm;
EvalGlobals *globals = _GLOBALS(_globals);
if (globals == NULL)
globals = eval_globals_default();
float4 color;
float3 normal;
const void *args[] = { coord, dxt, dyt, &cfra, &osatex };
void *results[] = { &color.x, &normal.x };
_FUNC_BVM(fn)->eval(_CTX(ctx), globals, args, results);
set_texresult(target, color, normal);
}
void BVM_eval_texture_llvm(struct BVMEvalGlobals *_globals, struct BVMEvalContext *UNUSED(ctx), struct BVMFunction *fn,
struct TexResult *value, struct TexResult *value_dx, struct TexResult *value_dy,
const float coord[3], const float dxt[3], const float dyt[3], int osatex,
short UNUSED(which_output), int cfra, int UNUSED(preview))
{
using namespace blenvm;
EvalGlobals *globals = _GLOBALS(_globals);
if (globals == NULL)
globals = eval_globals_default();
Dual2<float4> r_color;
Dual2<float3> r_normal;
#ifdef WITH_LLVM
TexNodesFunc fp = (TexNodesFunc)_FUNC_LLVM(fn)->ptr();
Dual2<float3> coord_v;
coord_v.set_value(float3(coord[0], coord[1], coord[2]));
if (dxt)
coord_v.set_dx(float3(dxt[0], dxt[1], dxt[2]));
else
coord_v.set_dx(float3(1.0f, 0.0f, 0.0f));
if (dyt)
coord_v.set_dy(float3(dyt[0], dyt[1], dyt[2]));
else
coord_v.set_dy(float3(0.0f, 1.0f, 0.0f));
fp(globals, &r_color, &r_normal, &coord_v, cfra, osatex);
#else
UNUSED_VARS(fn, globals, coord, dxt, dyt, cfra, osatex);
r_color = Dual2<float4>(float4(0.0f, 0.0f, 0.0f, 0.0f));
r_normal = Dual2<float3>(float3(0.0f, 0.0f, 1.0f));
#endif
if (value)
set_texresult(value, r_color.value(), r_normal.value());
if (value_dx)
set_texresult(value_dx, r_color.dx(), r_normal.dx());
if (value_dy)
set_texresult(value_dy, r_color.dy(), r_normal.dy());
}
/* ------------------------------------------------------------------------- */
namespace blenvm {
typedef void (*ModNodesFunc)(const struct EvalGlobals *globals,
mesh_ptr *result,
struct PointerRNA *object_ptr, struct PointerRNA *base_mesh_ptr);
}
struct BVMFunction *BVM_gen_modifier_function_bvm(struct bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_bvm(btree, use_cache, modifier_inputs, modifier_outputs);
}
struct BVMFunction *BVM_gen_modifier_function_llvm(struct bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_llvm(btree, use_cache, modifier_inputs, modifier_outputs);
}
void BVM_debug_modifier_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode)
{
using namespace blenvm;
debug_nodes(btree, debug_file, label, mode, modifier_inputs, modifier_outputs);
}
struct DerivedMesh *BVM_eval_modifier_bvm(struct BVMEvalGlobals *globals,
struct BVMEvalContext *ctx,
struct BVMFunction *fn,
struct Object *object,
struct Mesh *base_mesh)
{
using namespace blenvm;
PointerRNA object_ptr, base_mesh_ptr;
RNA_id_pointer_create((ID *)object, &object_ptr);
RNA_id_pointer_create((ID *)base_mesh, &base_mesh_ptr);
mesh_ptr result;
const void *args[] = { &object_ptr, &base_mesh_ptr };
void *results[] = { &result };
_FUNC_BVM(fn)->eval(_CTX(ctx), _GLOBALS(globals), args, results);
#if 0
DerivedMesh *dm = result.get();
#else
DerivedMesh *dm = CDDM_new(0, 0, 0, 0, 0);
#endif
/* destroy the pointer variable */
result.ptr().reset();
return dm;
}
struct DerivedMesh *BVM_eval_modifier_llvm(struct BVMEvalGlobals *_globals,
struct BVMEvalContext *UNUSED(ctx),
struct BVMFunction *fn,
struct Object *object,
struct Mesh *base_mesh)
{
using namespace blenvm;
EvalGlobals *globals = _GLOBALS(_globals);
if (globals == NULL)
globals = eval_globals_default();
mesh_ptr result;
#ifdef WITH_LLVM
PointerRNA object_ptr, base_mesh_ptr;
RNA_id_pointer_create((ID *)object, &object_ptr);
RNA_id_pointer_create((ID *)base_mesh, &base_mesh_ptr);
ModNodesFunc fp = (ModNodesFunc)_FUNC_LLVM(fn)->ptr();
fp(globals, &result, &object_ptr, &base_mesh_ptr);
#else
UNUSED_VARS(fn, globals, object, base_mesh);
result.set(CDDM_new(0, 0, 0, 0, 0));
#endif
DerivedMesh *dm = result.get();
/* destroy the pointer variable */
result.ptr().reset();
return dm;
}
/* ------------------------------------------------------------------------- */
struct BVMFunction *BVM_gen_dupli_function_bvm(struct bNodeTree *btree, bool use_cache)
{
using namespace blenvm;
return gen_function_bvm(btree, use_cache, dupli_inputs, dupli_outputs);
}
void BVM_debug_dupli_nodes(struct bNodeTree *btree, FILE *debug_file, const char *label, BVMDebugMode mode)
{
using namespace blenvm;
debug_nodes(btree, debug_file, label, mode, dupli_inputs, dupli_outputs);
}
void BVM_eval_dupli_bvm(struct BVMEvalGlobals *globals,
struct BVMEvalContext *ctx,
struct BVMFunction *fn,
struct Object *object,
struct DupliContainer *duplicont)
{
using namespace blenvm;
PointerRNA object_ptr;
RNA_id_pointer_create((ID *)object, &object_ptr);
const void *args[] = { &object_ptr };
duplis_ptr result;
void *results[] = { &result };
_FUNC_BVM(fn)->eval(_CTX(ctx), _GLOBALS(globals), args, results);
DupliList *duplis = result.get();
if (duplis) {
for (DupliList::const_iterator it = duplis->begin(); it != duplis->end(); ++it) {
const Dupli &dupli = *it;
BKE_dupli_add_instance(duplicont, dupli.object, (float (*)[4])dupli.transform.data, dupli.index,
false, dupli.hide, dupli.recursive);
}
}
result.reset();
}

View File

@@ -0,0 +1,73 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/function.cc
* \ingroup blenvm
*/
#include <assert.h>
#include "function.h"
#include "util_thread.h"
namespace blenvm {
static spin_lock users_lock = spin_lock();
FunctionBase::FunctionBase() :
m_users(0)
{
}
FunctionBase::~FunctionBase()
{
}
void FunctionBase::retain(FunctionBase *fn)
{
if (fn) {
users_lock.lock();
++fn->m_users;
users_lock.unlock();
}
}
bool FunctionBase::release(FunctionBase *fn)
{
bool released = false;
if (fn) {
users_lock.lock();
assert(fn->m_users > 0);
--fn->m_users;
released = (fn->m_users == 0);
users_lock.unlock();
}
return released;
}
} /* namespace blenvm */

View File

@@ -0,0 +1,58 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
/** \file blender/blenvm/intern/function.h
* \ingroup blenvm
*/
#include "MEM_guardedalloc.h"
namespace blenvm {
struct FunctionBase {
FunctionBase();
~FunctionBase();
/* Increment user count */
static void retain(FunctionBase *fn);
/* Decrement user count.
* Caller should delete the function if result is True!
*/
static bool release(FunctionBase *fn);
private:
int m_users;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:Function")
};
} /* namespace blenvm */
#endif /* __FUNCTION_H__ */

View File

@@ -0,0 +1,190 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/intern/function_cache.cc
* \ingroup blenvm
*/
#include "MEM_guardedalloc.h"
#include "function_cache.h"
#include "bvm_function.h"
#include "llvm_function.h"
namespace blenvm {
template <typename T>
struct FunctionCache {
typedef T function_type;
typedef unordered_map<void*, function_type*> FunctionMap;
typedef typename FunctionMap::iterator iterator;
typedef typename FunctionMap::const_iterator const_iterator;
FunctionMap m_functions;
function_type *acquire(void *key)
{
const_iterator it = m_functions.find(key);
function_type *fn = NULL;
if (it != m_functions.end()) {
fn = it->second;
}
return fn;
}
void release(function_type *fn)
{
if (!fn)
return;
if (function_type::release(fn)) {
iterator it = m_functions.begin();
while (it != m_functions.end()) {
if (it->second == fn) {
iterator it_del = it++;
m_functions.erase(it_del);
}
else
++it;
}
delete fn;
}
}
void set(void *key, function_type *fn)
{
typedef std::pair<void*, function_type*> FunctionCachePair;
if (fn) {
iterator it = m_functions.find(key);
if (it == m_functions.end()) {
function_type::retain(fn);
m_functions.insert(FunctionCachePair(key, fn));
}
else if (fn != it->second) {
if (function_type::release(it->second))
delete it->second;
function_type::retain(fn);
it->second = fn;
}
}
else {
iterator it = m_functions.find(key);
if (it != m_functions.end()) {
if (function_type::release(it->second))
delete it->second;
m_functions.erase(it);
}
}
}
void remove(void *key)
{
iterator it = m_functions.find(key);
if (it != m_functions.end()) {
if (function_type::release(it->second))
delete it->second;
m_functions.erase(it);
}
}
void clear(void)
{
for (iterator it = m_functions.begin(); it != m_functions.end(); ++it) {
if (function_type::release(it->second))
delete it->second;
}
m_functions.clear();
}
};
/* BVM cache */
static FunctionCache<FunctionBVM> bvm_function_cache;
FunctionBVM *function_bvm_cache_acquire(void *key)
{
return bvm_function_cache.acquire(key);
}
void function_bvm_cache_release(FunctionBVM *fn)
{
bvm_function_cache.release(fn);
}
void function_bvm_cache_set(void *key, FunctionBVM *fn)
{
bvm_function_cache.set(key, fn);
}
void function_bvm_cache_remove(void *key)
{
bvm_function_cache.remove(key);
}
void function_bvm_cache_clear(void)
{
bvm_function_cache.clear();
}
#ifdef WITH_LLVM
/* LLVM cache */
static FunctionCache<FunctionLLVM> llvm_function_cache;
FunctionLLVM *function_llvm_cache_acquire(void *key)
{
return llvm_function_cache.acquire(key);
}
void function_llvm_cache_release(FunctionLLVM *fn)
{
llvm_function_cache.release(fn);
}
void function_llvm_cache_set(void *key, FunctionLLVM *fn)
{
llvm_function_cache.set(key, fn);
}
void function_llvm_cache_remove(void *key)
{
llvm_function_cache.remove(key);
}
void function_llvm_cache_clear(void)
{
llvm_function_cache.clear();
}
#endif
} /* namespace blenvm */

View File

@@ -0,0 +1,59 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __FUNCTION_CACHE_H__
#define __FUNCTION_CACHE_H__
/** \file blender/blenvm/intern/function_cache.h
* \ingroup blenvm
*/
#include "util_map.h"
namespace blenvm {
struct FunctionBVM;
FunctionBVM *function_bvm_cache_acquire(void *key);
void function_bvm_cache_release(FunctionBVM *fn);
void function_bvm_cache_set(void *key, FunctionBVM *fn);
void function_bvm_cache_remove(void *key);
void function_bvm_cache_clear(void);
#ifdef WITH_LLVM
struct FunctionLLVM;
FunctionLLVM *function_llvm_cache_acquire(void *key);
void function_llvm_cache_release(FunctionLLVM *fn);
void function_llvm_cache_set(void *key, FunctionLLVM *fn);
void function_llvm_cache_remove(void *key);
void function_llvm_cache_clear(void);
#endif
} /* namespace blenvm */
#endif /* __FUNCTION_CACHE_H__ */

View File

@@ -0,0 +1,63 @@
# ***** 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.
#
# The Original Code is Copyright (C) 2015, Blender Foundation
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): Lukas Toenne.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
..
../compile
../intern
../modules
../util
../../blenkernel
../../blenlib
../../imbuf
../../makesdna
../../makesrna
../../../../intern/guardedalloc
)
set(INC_SYS
${LLVM_INCLUDE_DIRS}
)
set(SRC
llvm_codegen.cc
llvm_codegen.h
llvm_engine.cc
llvm_engine.h
llvm_function.cc
llvm_function.h
llvm_headers.h
llvm_modules.cc
llvm_modules.h
llvm_types.cc
llvm_types.h
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RTTI_DISABLE_FLAGS}")
add_definitions(-DWITH_LLVM -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -std=c++0x)
blender_add_lib(bf_blenvm_llvm "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -0,0 +1,878 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/llvm/llvm_codegen.cc
* \ingroup llvm
*/
#include <cctype>
#include <cstdio>
#include <set>
#include <sstream>
#include "node_graph.h"
#include "llvm_codegen.h"
#include "llvm_engine.h"
#include "llvm_function.h"
#include "llvm_headers.h"
#include "llvm_modules.h"
#include "llvm_types.h"
#include "util_opcode.h"
#include "modules.h"
namespace blenvm {
/* llvm::ostream that writes to a FILE. */
class file_ostream : public llvm::raw_ostream {
FILE *file;
/* write_impl - See raw_ostream::write_impl. */
void write_impl(const char *ptr, size_t size) override {
fwrite(ptr, sizeof(char), size, file);
}
/* current_pos - Return the current position within the stream, not
* counting the bytes currently in the buffer.
*/
uint64_t current_pos() const override { return ftell(file); }
public:
explicit file_ostream(FILE *f) : file(f) {}
~file_ostream() {
fflush(file);
}
};
class debug_assembly_annotation_writer : public llvm::AssemblyAnnotationWriter
{
/* add implementation here if needed */
};
/* ------------------------------------------------------------------------- */
/* replace non-alphanumeric chars with underscore */
static string sanitize_name(const string &name)
{
string s = name;
for (string::iterator it = s.begin(); it != s.end(); ++it) {
char &c = *it;
if (c != '_' && !isalnum(c))
c = '_';
}
return s;
}
/* forward declaration */
static llvm::Function *declare_graph_function(
llvm::LLVMContext &context,
llvm::Module *mod,
const string &name,
const NodeGraph *graph,
bool use_globals);
static void define_node_function(
llvm::LLVMContext &context,
llvm::Module *mod,
OpCode op,
const string &nodetype_name);
llvm::Module *LLVMCodeGenerator::m_nodes_module = NULL;
LLVMCodeGenerator::LLVMCodeGenerator(int opt_level) :
m_opt_level(opt_level),
m_function_address(0),
m_module(NULL),
m_function(NULL),
m_block(NULL)
{
using namespace llvm;
BLI_assert(m_nodes_module != NULL);
}
LLVMCodeGenerator::~LLVMCodeGenerator()
{
using namespace llvm;
destroy_module();
}
void LLVMCodeGenerator::define_nodes_module(llvm::LLVMContext &context)
{
using namespace llvm;
m_nodes_module = new llvm::Module("texture_nodes", context);
#define DEF_OPCODE(op) \
define_node_function(context, m_nodes_module, OP_##op, STRINGIFY(op));
BVM_DEFINE_OPCODES
#undef DEF_OPCODE
}
llvm::LLVMContext &LLVMCodeGenerator::context() const
{
return llvm::getGlobalContext();
}
ValueHandle LLVMCodeGenerator::get_handle(const DualValue &value)
{
return value.value();
}
void LLVMCodeGenerator::create_module(const string &name)
{
using namespace llvm;
std::string error;
BLI_assert(m_nodes_module != NULL);
/* create an empty module */
m_module = new llvm::Module(name, context());
/* link the node functions module, so we can call those functions */
Linker::LinkModules(m_module, m_nodes_module, Linker::LinkerMode::PreserveSource, &error);
verifyModule(*m_module, &outs());
}
void LLVMCodeGenerator::destroy_module()
{
if (m_module != NULL) {
delete m_module;
m_module = NULL;
}
}
void LLVMCodeGenerator::finalize_function()
{
using namespace llvm;
llvm_optimize_module(m_module, m_opt_level);
llvm_optimize_function(m_function, m_opt_level);
verifyFunction(*m_function, &outs());
verifyModule(*m_module, &outs());
/* Note: Adding module to exec engine before creating the function prevents compilation! */
llvm_execution_engine()->addModule(m_module);
llvm_execution_engine()->generateCodeForModule(m_module);
m_function_address = llvm_execution_engine()->getFunctionAddress(m_function->getName());
BLI_assert(m_function_address != 0);
llvm_execution_engine()->removeModule(m_module);
destroy_module();
m_function = NULL;
}
void LLVMCodeGenerator::debug_function(FILE *file)
{
using namespace llvm;
llvm_optimize_module(m_module, m_opt_level);
llvm_optimize_function(m_function, m_opt_level);
verifyFunction(*m_function, &outs());
verifyModule(*m_module, &outs());
file_ostream stream(file);
debug_assembly_annotation_writer aaw;
m_module->print(stream, &aaw);
destroy_module();
}
void LLVMCodeGenerator::node_graph_begin(const string &name, const NodeGraph *graph, bool use_globals)
{
using namespace llvm;
create_module(name);
m_function = declare_graph_function(context(), m_module, name, graph, use_globals);
/* map function arguments */
{
size_t num_inputs = graph->inputs.size();
size_t num_outputs = graph->outputs.size();
Function::ArgumentListType::iterator arg_it = m_function->arg_begin();
m_globals_ptr = arg_it++; /* globals, passed to functions which need it */
for (int i = 0; i < num_outputs; ++i)
m_output_args.push_back(arg_it++);
for (int i = 0; i < num_inputs; ++i)
m_input_args.push_back(arg_it++);
}
m_block = BasicBlock::Create(context(), "entry", m_function);
}
void LLVMCodeGenerator::node_graph_end()
{
using namespace llvm;
BLI_assert(m_function != NULL);
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
builder.CreateRetVoid();
m_values.clear();
}
void LLVMCodeGenerator::store_return_value(size_t output_index, const TypeSpec *typespec, ValueHandle handle)
{
using namespace llvm;
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
Argument *arg = m_output_args[output_index];
DualValue dval = m_values.at(handle);
if (bvm_type_has_dual_value(typespec)) {
Value *value_ptr = builder.CreateStructGEP(arg, 0, sanitize_name(arg->getName().str() + "_V"));
Value *dx_ptr = builder.CreateStructGEP(arg, 1, sanitize_name(arg->getName().str() + "_DX"));
Value *dy_ptr = builder.CreateStructGEP(arg, 2, sanitize_name(arg->getName().str() + "_DY"));
bvm_llvm_copy_value(context(), m_block, value_ptr, dval.value(), typespec);
bvm_llvm_copy_value(context(), m_block, dx_ptr, dval.dx(), typespec);
bvm_llvm_copy_value(context(), m_block, dy_ptr, dval.dy(), typespec);
}
else {
bvm_llvm_copy_value(context(), m_block, arg, dval.value(), typespec);
}
}
ValueHandle LLVMCodeGenerator::map_argument(size_t input_index, const TypeSpec *typespec)
{
using namespace llvm;
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
Argument *arg = m_input_args[input_index];
DualValue dval;
if (bvm_type_has_dual_value(typespec)) {
/* argument is a struct, use GEP instructions to get the individual elements */
dval = DualValue(builder.CreateStructGEP(arg, 0, sanitize_name(arg->getName().str() + "_V")),
builder.CreateStructGEP(arg, 1, sanitize_name(arg->getName().str() + "_DX")),
builder.CreateStructGEP(arg, 2, sanitize_name(arg->getName().str() + "_DY")));
}
else {
dval = DualValue(arg, NULL, NULL);
}
ValueHandle handle = get_handle(dval);
bool ok = m_values.insert(HandleValueMap::value_type(handle, dval)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
ValueHandle LLVMCodeGenerator::alloc_node_value(const TypeSpec *typespec, const string &name)
{
using namespace llvm;
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
Type *type = bvm_get_llvm_type(context(), typespec, false);
BLI_assert(type != NULL);
DualValue dval(builder.CreateAlloca(type, NULL, sanitize_name(name + "_V")),
builder.CreateAlloca(type, NULL, sanitize_name(name + "_DX")),
builder.CreateAlloca(type, NULL, sanitize_name(name + "_DY")));
ValueHandle handle = get_handle(dval);
bool ok = m_values.insert(HandleValueMap::value_type(handle, dval)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
ValueHandle LLVMCodeGenerator::create_constant(const TypeSpec *UNUSED(typespec), const NodeConstant *node_value)
{
using namespace llvm;
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
/* create storage for the global value */
Constant *cvalue = bvm_create_llvm_constant(context(), node_value);
AllocaInst *pvalue = builder.CreateAlloca(cvalue->getType());
/* XXX this may not work for larger aggregate types (matrix44) !! */
builder.CreateStore(cvalue, pvalue);
DualValue dval(pvalue);
ValueHandle handle = get_handle(dval);
bool ok = m_values.insert(HandleValueMap::value_type(handle, dval)).second;
BLI_assert(ok && "Could not insert value!");
UNUSED_VARS(ok);
return handle;
}
void LLVMCodeGenerator::eval_node(const NodeType *nodetype,
ArrayRef<ValueHandle> input_args,
ArrayRef<ValueHandle> output_args)
{
using namespace llvm;
IRBuilder<> builder(context());
builder.SetInsertPoint(m_block);
/* call evaluation function */
Function *evalfunc = m_module->getFunction(nodetype->name());
BLI_assert(evalfunc != NULL && "Could not find node function!");
std::vector<Value*> evalargs;
if (nodetype->use_globals()) {
evalargs.push_back(m_globals_ptr);
}
for (int i = 0; i < nodetype->num_outputs(); ++i) {
const NodeOutput *output = nodetype->find_output(i);
const TypeSpec *typespec = output->typedesc.get_typespec();
const DualValue &dval = m_values.at(output_args[i]);
if (bvm_type_has_dual_value(typespec)) {
evalargs.push_back(dval.value());
evalargs.push_back(dval.dx());
evalargs.push_back(dval.dy());
}
else {
evalargs.push_back(dval.value());
}
}
for (int i = 0; i < nodetype->num_inputs(); ++i) {
const NodeInput *input = nodetype->find_input(i);
const TypeSpec *typespec = input->typedesc.get_typespec();
bool is_constant = (input->value_type == INPUT_CONSTANT);
const DualValue &dval = m_values.at(input_args[i]);
if (!is_constant && bvm_type_has_dual_value(typespec)) {
if (typespec->is_aggregate() || typespec->is_structure()) {
evalargs.push_back(dval.value());
evalargs.push_back(dval.dx());
evalargs.push_back(dval.dy());
}
else {
/* pass-by-value for non-aggregate types */
evalargs.push_back(builder.CreateLoad(dval.value()));
evalargs.push_back(builder.CreateLoad(dval.dx()));
evalargs.push_back(builder.CreateLoad(dval.dy()));
}
}
else {
if (typespec->is_aggregate() || typespec->is_structure()) {
evalargs.push_back(dval.value());
}
else {
/* pass-by-value for non-aggregate types */
evalargs.push_back(builder.CreateLoad(dval.value()));
}
}
}
CallInst *call = builder.CreateCall(evalfunc, evalargs);
UNUSED_VARS(call);
}
/* ------------------------------------------------------------------------- */
static void append_graph_input_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeGraph::Input *input)
{
using namespace llvm;
const TypeSpec *spec = input->typedesc.get_typespec();
llvm::Type *type = bvm_get_llvm_type(context, spec, true);
if (bvm_type_has_dual_value(spec) || spec->is_aggregate() || spec->is_structure())
type = type->getPointerTo();
arg_types.push_back(type);
arg_names.push_back(input->name);
}
static void append_graph_output_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeGraph::Output *output)
{
using namespace llvm;
const TypeSpec *spec = output->typedesc.get_typespec();
Type *type = bvm_get_llvm_type(context, spec, true);
/* return argument is a pointer */
type = type->getPointerTo();
arg_types.push_back(type);
arg_names.push_back(output->name);
}
static llvm::Function *declare_graph_function(
llvm::LLVMContext &context,
llvm::Module *mod,
const string &name,
const NodeGraph *graph,
bool use_globals)
{
using namespace llvm;
std::vector<Type *> arg_types;
std::vector<string> arg_names;
if (use_globals) {
Type *t_globals = llvm::TypeBuilder<void*, false>::get(context);
arg_types.push_back(t_globals);
arg_names.push_back("globals");
}
for (int i = 0; i < graph->outputs.size(); ++i) {
const NodeGraph::Output *output = graph->get_output(i);
append_graph_output_args(context, arg_types, arg_names, output);
}
for (int i = 0; i < graph->inputs.size(); ++i) {
const NodeGraph::Input *input = graph->get_input(i);
append_graph_input_args(context, arg_types, arg_names, input);
}
FunctionType *functype = FunctionType::get(TypeBuilder<void, true>::get(context), arg_types, false);
Function *func = Function::Create(functype, Function::ExternalLinkage, name, mod);
Function::arg_iterator arg_it = func->arg_begin();
for (int i = 0; arg_it != func->arg_end(); ++arg_it, ++i) {
arg_it->setName(sanitize_name(arg_names[i]));
}
return func;
}
static void append_node_input_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeInput *input)
{
using namespace llvm;
const TypeSpec *spec = input->typedesc.get_typespec();
bool is_constant = (input->value_type == INPUT_CONSTANT);
Type *type = bvm_get_llvm_type(context, spec, false);
/* pass-by-reference for aggregate types */
if (spec->is_aggregate() || spec->is_structure())
type = type->getPointerTo();
if (!is_constant && bvm_type_has_dual_value(spec)) {
arg_types.push_back(type);
arg_names.push_back("V_" + input->name);
/* two derivatives */
arg_types.push_back(type);
arg_names.push_back("DX_" + input->name);
arg_types.push_back(type);
arg_names.push_back("DY_" + input->name);
}
else {
arg_types.push_back(type);
arg_names.push_back(input->name);
}
}
static void append_node_output_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeOutput *output)
{
using namespace llvm;
const TypeSpec *spec = output->typedesc.get_typespec();
Type *type = bvm_get_llvm_type(context, spec, false);
/* return argument is a pointer */
type = type->getPointerTo();
if (bvm_type_has_dual_value(spec)) {
arg_types.push_back(type);
arg_names.push_back("V_" + output->name);
/* two derivatives */
arg_types.push_back(type);
arg_names.push_back("DX_" + output->name);
arg_types.push_back(type);
arg_names.push_back("DY_" + output->name);
}
else {
arg_types.push_back(type);
arg_names.push_back(output->name);
}
}
static llvm::Function *declare_node_function(
llvm::LLVMContext &context,
llvm::Module *mod,
const NodeType *nodetype)
{
using namespace llvm;
std::vector<Type *> arg_types;
std::vector<string> arg_names;
if (nodetype->use_globals()) {
Type *t_globals = llvm::TypeBuilder<void*, false>::get(context);
arg_types.push_back(t_globals);
arg_names.push_back("globals");
}
for (int i = 0; i < nodetype->num_outputs(); ++i) {
const NodeOutput *output = nodetype->find_output(i);
append_node_output_args(context, arg_types, arg_names, output);
}
for (int i = 0; i < nodetype->num_inputs(); ++i) {
const NodeInput *input = nodetype->find_input(i);
append_node_input_args(context, arg_types, arg_names, input);
}
FunctionType *functype = FunctionType::get(TypeBuilder<void, true>::get(context), arg_types, false);
Function *func = Function::Create(functype, Function::ExternalLinkage, nodetype->name(), mod);
Function::arg_iterator arg_it = func->arg_begin();
for (int i = 0; arg_it != func->arg_end(); ++arg_it, ++i) {
arg_it->setName(sanitize_name(arg_names[i]));
}
return func;
}
static void append_elementary_input_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeInput *input, bool with_derivs)
{
using namespace llvm;
const TypeSpec *spec = input->typedesc.get_typespec();
bool is_constant = (input->value_type == INPUT_CONSTANT);
Type *type = bvm_get_llvm_type(context, spec, false);
/* pass-by-reference for aggregate types */
if (spec->is_aggregate() || spec->is_structure())
type = type->getPointerTo();
if (!is_constant && with_derivs && bvm_type_has_dual_value(spec)) {
arg_types.push_back(type);
arg_names.push_back("V_" + input->name);
/* partial derivative */
arg_types.push_back(type);
arg_names.push_back("D_" + input->name);
}
else {
arg_types.push_back(type);
arg_names.push_back(input->name);
}
}
static void append_elementary_output_args(llvm::LLVMContext &context,
std::vector<llvm::Type*> &arg_types,
std::vector<string> &arg_names,
const NodeOutput *output)
{
using namespace llvm;
const TypeSpec *spec = output->typedesc.get_typespec();
Type *type = bvm_get_llvm_type(context, spec, false);
/* return argument is a pointer */
type = type->getPointerTo();
arg_types.push_back(type);
arg_names.push_back(output->name);
}
static llvm::Function *declare_elementary_node_function(
llvm::LLVMContext &context,
llvm::Module *mod,
const NodeType *nodetype,
const string &name,
bool with_input_derivs)
{
using namespace llvm;
std::vector<Type *> arg_types;
std::vector<string> arg_names;
if (nodetype->use_globals()) {
Type *t_globals = llvm::TypeBuilder<void*, false>::get(context);
arg_types.push_back(t_globals);
arg_names.push_back("globals");
}
for (int i = 0; i < nodetype->num_outputs(); ++i) {
const NodeOutput *output = nodetype->find_output(i);
append_elementary_output_args(context, arg_types, arg_names, output);
}
for (int i = 0; i < nodetype->num_inputs(); ++i) {
const NodeInput *input = nodetype->find_input(i);
append_elementary_input_args(context, arg_types, arg_names, input, with_input_derivs);
}
FunctionType *functype = FunctionType::get(TypeBuilder<void, true>::get(context), arg_types, false);
Function *func = Function::Create(functype, Function::ExternalLinkage, name, mod);
Function::arg_iterator arg_it = func->arg_begin();
for (int i = 0; arg_it != func->arg_end(); ++arg_it, ++i) {
arg_it->setName(sanitize_name(arg_names[i]));
}
return func;
}
static void define_elementary_functions(
llvm::LLVMContext &context,
llvm::Module *mod,
OpCode op,
const NodeType *nodetype)
{
using namespace llvm;
/* declare functions */
Function *value_func = NULL, *deriv_func = NULL;
if (llvm_has_external_impl_value(op)) {
value_func = declare_elementary_node_function(
context, mod, nodetype,
bvm_value_function_name(nodetype->name()), false);
}
if (llvm_has_external_impl_deriv(op)) {
deriv_func = declare_elementary_node_function(
context, mod, nodetype,
bvm_deriv_function_name(nodetype->name()), true);
}
UNUSED_VARS(value_func, deriv_func);
}
static void define_dual_function_wrapper(
llvm::LLVMContext &context,
llvm::Module *mod,
llvm::Function *func,
OpCode UNUSED(op),
const NodeType *nodetype)
{
using namespace llvm;
/* get evaluation function(s) */
Function *value_func = mod->getFunction(bvm_value_function_name(nodetype->name()));
BLI_assert(value_func != NULL && "Could not find node function!");
Function *deriv_func = mod->getFunction(bvm_deriv_function_name(nodetype->name()));
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
/* collect arguments for calling internal elementary functions */
/* arguments for calculating main value and partial derivatives */
std::vector<Value*> call_args_value, call_args_dx, call_args_dy;
Function::arg_iterator arg_it = func->arg_begin();
if (nodetype->use_globals()) {
Value *globals = arg_it++;
call_args_value.push_back(globals);
call_args_dx.push_back(globals);
call_args_dy.push_back(globals);
}
/* output arguments */
for (int i = 0; i < nodetype->num_outputs(); ++i) {
const NodeOutput *output = nodetype->find_output(i);
Value *val, *dx, *dy;
if (bvm_type_has_dual_value(output->typedesc.get_typespec())) {
val = arg_it++;
dx = arg_it++;
dy = arg_it++;
}
else {
val = arg_it++;
dx = NULL;
dy = NULL;
}
call_args_value.push_back(val);
if (dx != NULL)
call_args_dx.push_back(dx);
if (dy != NULL)
call_args_dy.push_back(dy);
}
/* input arguments */
for (int i = 0; i < nodetype->num_inputs(); ++i) {
const NodeInput *input = nodetype->find_input(i);
const TypeSpec *typespec = input->typedesc.get_typespec();
Value *val, *dx, *dy;
if (input->value_type != INPUT_CONSTANT && bvm_type_has_dual_value(typespec)) {
val = arg_it++;
dx = arg_it++;
dy = arg_it++;
}
else {
val = arg_it++;
dx = NULL;
dy = NULL;
}
call_args_value.push_back(val);
/* derivative functions take input value as well as its derivative */
call_args_dx.push_back(val);
if (dx != NULL)
call_args_dx.push_back(dx);
call_args_dy.push_back(val);
if (dy != NULL)
call_args_dy.push_back(dy);
}
BLI_assert(arg_it == func->arg_end() && "Did not use all the function arguments!");
/* calculate value */
builder.CreateCall(value_func, call_args_value);
if (deriv_func) {
builder.CreateCall(deriv_func, call_args_dx);
builder.CreateCall(deriv_func, call_args_dy);
}
else {
/* zero the derivatives */
for (int i = 0; i < nodetype->num_outputs(); ++i) {
const NodeOutput *output = nodetype->find_output(i);
const TypeSpec *typespec = output->typedesc.get_typespec();
if (bvm_type_has_dual_value(typespec)) {
int arg_i = nodetype->use_globals() ? i + 1 : i;
bvm_llvm_set_zero(context, block, call_args_dx[arg_i], typespec);
bvm_llvm_set_zero(context, block, call_args_dy[arg_i], typespec);
}
}
}
builder.CreateRetVoid();
}
static void define_node_function(
llvm::LLVMContext &context,
llvm::Module *mod,
OpCode op,
const string &nodetype_name)
{
using namespace llvm;
const NodeType *nodetype = NodeGraph::find_node_type(nodetype_name);
if (nodetype == NULL)
return;
/* wrapper function */
Function *func = declare_node_function(context, mod, nodetype);
if (func == NULL)
return;
switch (op) {
/* special cases */
case OP_GET_DERIVATIVE_FLOAT:
def_node_GET_DERIVATIVE_FLOAT(context, func);
break;
case OP_GET_DERIVATIVE_FLOAT3:
def_node_GET_DERIVATIVE_FLOAT3(context, func);
break;
case OP_GET_DERIVATIVE_FLOAT4:
def_node_GET_DERIVATIVE_FLOAT4(context, func);
break;
case OP_VALUE_FLOAT:
def_node_VALUE_FLOAT(context, func);
break;
case OP_VALUE_INT:
def_node_VALUE_INT(context, func);
break;
case OP_VALUE_FLOAT3:
def_node_VALUE_FLOAT3(context, func);
break;
case OP_VALUE_FLOAT4:
def_node_VALUE_FLOAT4(context, func);
break;
case OP_VALUE_MATRIX44:
def_node_VALUE_MATRIX44(context, func);
break;
case OP_FLOAT_TO_INT:
def_node_FLOAT_TO_INT(context, func);
break;
case OP_INT_TO_FLOAT:
def_node_INT_TO_FLOAT(context, func);
break;
case OP_SET_FLOAT3:
def_node_SET_FLOAT3(context, func);
break;
case OP_GET_ELEM_FLOAT3:
def_node_GET_ELEM_FLOAT3(context, func);
break;
case OP_SET_FLOAT4:
def_node_SET_FLOAT4(context, func);
break;
case OP_GET_ELEM_FLOAT4:
def_node_GET_ELEM_FLOAT4(context, func);
break;
default:
define_elementary_functions(context, mod, op, nodetype);
define_dual_function_wrapper(context, mod, func, op, nodetype);
break;
}
}
} /* namespace blenvm */

View File

@@ -0,0 +1,123 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_CODEGEN_H__
#define __LLVM_CODEGEN_H__
/** \file blender/blenvm/llvm/llvm_codegen.h
* \ingroup llvm
*/
#include <set>
#include <vector>
#include "MEM_guardedalloc.h"
#include "compiler.h"
#include "node_graph.h"
#include "util_opcode.h"
#include "util_string.h"
#include "util_math.h"
namespace llvm {
class LLVMContext;
class Argument;
class BasicBlock;
class CallInst;
class Constant;
class Function;
class FunctionType;
class Module;
class StructType;
class Type;
class Value;
}
namespace blenvm {
struct NodeGraph;
struct NodeInstance;
struct TypeDesc;
struct FunctionLLVM;
struct LLVMCodeGenerator : public CodeGenerator {
typedef Dual2<llvm::Value*> DualValue;
typedef std::map<ValueHandle, DualValue> HandleValueMap;
typedef std::vector<llvm::Argument*> OutputArguments;
typedef std::vector<llvm::Argument*> InputArguments;
LLVMCodeGenerator(int opt_level);
~LLVMCodeGenerator();
static void define_nodes_module(llvm::LLVMContext &context);
uint64_t function_address() const { return m_function_address; }
void finalize_function();
void debug_function(FILE *file);
void node_graph_begin(const string &name, const NodeGraph *graph, bool use_globals);
void node_graph_end();
void store_return_value(size_t output_index, const TypeSpec *typespec, ValueHandle value);
ValueHandle map_argument(size_t input_index, const TypeSpec *typespec);
ValueHandle alloc_node_value(const TypeSpec *typespec, const string &name);
ValueHandle create_constant(const TypeSpec *typespec, const NodeConstant *node_value);
void eval_node(const NodeType *nodetype,
ArrayRef<ValueHandle> input_args,
ArrayRef<ValueHandle> output_args);
protected:
llvm::LLVMContext &context() const;
static ValueHandle get_handle(const DualValue &value);
void create_module(const string &name);
void destroy_module();
private:
static llvm::Module *m_nodes_module;
int m_opt_level;
uint64_t m_function_address;
llvm::Module *m_module;
llvm::Function *m_function;
llvm::Argument *m_globals_ptr;
InputArguments m_input_args;
OutputArguments m_output_args;
llvm::BasicBlock *m_block;
HandleValueMap m_values;
};
} /* namespace blenvm */
#endif /* __LLVM_COMPILER_H__ */

View File

@@ -0,0 +1,235 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/llvm/llvm_engine.cc
* \ingroup llvm
*/
#include "MEM_guardedalloc.h"
extern "C" {
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_string.h"
}
#include "util_opcode.h"
#include "llvm_codegen.h"
#include "llvm_engine.h"
#include "llvm_headers.h"
#include "llvm_modules.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
namespace blenvm {
static llvm::ExecutionEngine *theEngine = NULL;
static llvm::Module *theModule = NULL;
static llvm::legacy::PassManager *theModulePassMgr[3] = {0};
static llvm::legacy::FunctionPassManager *theFunctionPassMgr[3] = {0};
static struct GHash *extern_functions = NULL;
class MemoryManager : public llvm::SectionMemoryManager {
public:
MemoryManager()
{}
virtual ~MemoryManager()
{}
/// This method returns the address of the specified function or variable.
/// It is used to resolve symbols during module linking.
uint64_t getSymbolAddress(const std::string &Name) override
{
uint64_t addr = llvm::SectionMemoryManager::getSymbolAddress(Name);
if (addr)
return addr;
void *ptr = BLI_ghash_lookup(extern_functions, Name.c_str());
return (uint64_t)ptr;
}
};
static llvm::ExecutionEngine *create_execution_engine()
{
using namespace llvm;
std::string error;
Module *mod = new Module("main", getGlobalContext());
theModule = mod;
EngineBuilder builder = EngineBuilder(mod);
builder.setEngineKind(EngineKind::JIT);
builder.setUseMCJIT(true);
builder.setErrorStr(&error);
builder.setMCJITMemoryManager(new MemoryManager());
ExecutionEngine *engine = builder.create();
if (!engine) {
printf("Could not create ExecutionEngine: %s\n", error.c_str());
}
return engine;
}
static void create_pass_managers()
{
using namespace llvm;
using legacy::FunctionPassManager;
using legacy::PassManager;
BLI_assert(theModule != NULL && "theModule must be initialized for pass managers!");
for (int opt_level = 0; opt_level < 3; ++opt_level) {
theModulePassMgr[opt_level] = new PassManager();
theFunctionPassMgr[opt_level] = new FunctionPassManager(theModule);
PassManager &MPM = *theModulePassMgr[opt_level];
FunctionPassManager &FPM = *theFunctionPassMgr[opt_level];
#if 0
/* Set up the optimizer pipeline.
* Start with registering info about how the
* target lays out data structures.
*/
FPM.add(new DataLayoutPass(*llvm_execution_engine()->getDataLayout()));
/* Provide basic AliasAnalysis support for GVN. */
FPM.add(createBasicAliasAnalysisPass());
/* Do simple "peephole" optimizations and bit-twiddling optzns. */
FPM.add(createInstructionCombiningPass());
/* Reassociate expressions. */
FPM.add(createReassociatePass());
/* Eliminate Common SubExpressions. */
FPM.add(createGVNPass());
/* Simplify the control flow graph (deleting unreachable blocks, etc). */
FPM.add(createCFGSimplificationPass());
FPM.doInitialization();
#endif
PassManagerBuilder builder;
builder.OptLevel = opt_level;
builder.populateModulePassManager(MPM);
if (opt_level > 1) {
/* Inline small functions */
MPM.add(createFunctionInliningPass());
}
builder.populateFunctionPassManager(FPM);
if (opt_level > 1) {
/* Optimize memcpy intrinsics */
FPM.add(createMemCpyOptPass());
}
}
}
void llvm_init()
{
using namespace llvm;
extern_functions = BLI_ghash_str_new("LLVM external functions hash");
register_extern_node_functions();
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
BLI_assert(theEngine == NULL);
theEngine = create_execution_engine();
create_pass_managers();
LLVMCodeGenerator::define_nodes_module(getGlobalContext());
}
static void extern_functions_keyfree(void *key)
{
MEM_freeN(key);
}
void llvm_free()
{
if (theEngine) {
delete theEngine;
theEngine = NULL;
}
for (int opt_level = 0; opt_level < 3; ++opt_level) {
if (theModulePassMgr[opt_level])
delete theModulePassMgr[opt_level];
if (theFunctionPassMgr[opt_level])
delete theFunctionPassMgr[opt_level];
}
BLI_ghash_free(extern_functions, extern_functions_keyfree, NULL);
}
llvm::ExecutionEngine *llvm_execution_engine()
{
return theEngine;
}
void llvm_register_external_function(const string &name, void *func)
{
char *namebuf = BLI_strdup(name.c_str());
BLI_ghash_insert(extern_functions, namebuf, func);
}
bool llvm_has_external_function(const string &name)
{
return BLI_ghash_haskey(extern_functions, name.c_str());
}
void llvm_optimize_module(llvm::Module *mod, int opt_level)
{
using namespace llvm;
BLI_assert(opt_level >= 0 && opt_level <= 3 && "Invalid optimization level (must be between 0 and 3)");
PassManager &MPM = *theModulePassMgr[opt_level];
mod->setDataLayout(theEngine->getDataLayout());
mod->setTargetTriple(theEngine->getTargetMachine()->getTargetTriple());
MPM.run(*mod);
}
void llvm_optimize_function(llvm::Function *func, int opt_level)
{
using namespace llvm;
BLI_assert(opt_level >= 0 && opt_level <= 3 && "Invalid optimization level (must be between 0 and 3)");
FunctionPassManager &FPM = *theFunctionPassMgr[opt_level];
FPM.run(*func);
}
} /* namespace llvm */

View File

@@ -0,0 +1,59 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_ENGINE_H__
#define __LLVM_ENGINE_H__
/** \file blender/blenvm/llvm/llvm_engine.h
* \ingroup llvm
*/
#include "util_string.h"
#include "util_opcode.h"
namespace llvm {
class ExecutionEngine;
class Function;
class Module;
}
namespace blenvm {
void llvm_init();
void llvm_free();
llvm::ExecutionEngine *llvm_execution_engine();
void llvm_register_external_function(const string &name, void *func);
bool llvm_has_external_function(const string &name);
void llvm_optimize_module(llvm::Module *mod, int opt_level);
void llvm_optimize_function(llvm::Function *func, int opt_level);
} /* namespace blenvm */
#endif /* __LLVM_ENGINE_H__ */

View File

@@ -0,0 +1,41 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/llvm/llvm_function.cc
* \ingroup llvm
*/
#include "llvm_function.h"
namespace blenvm {
FunctionLLVM::FunctionLLVM(uint64_t address) :
m_address(address)
{
}
} /* namespace llvm */

View File

@@ -0,0 +1,59 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_FUNCTION_H__
#define __LLVM_FUNCTION_H__
/** \file blender/blenvm/llvm/llvm_function.h
* \ingroup llvm
*/
#include "MEM_guardedalloc.h"
#include "function.h"
#include "llvm_engine.h"
#include "util_opcode.h"
namespace blenvm {
struct FunctionLLVM : public FunctionBase {
FunctionLLVM(uint64_t address);
uint64_t address() const { return m_address; }
void *ptr() const { return (void *)m_address; }
private:
uint64_t m_address;
MEM_CXX_CLASS_ALLOC_FUNCS("BVM:FunctionLLVM")
};
} /* namespace blenvm */
#endif /* __LLVM_FUNCTION_H__ */

View File

@@ -0,0 +1,59 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_HEADERS_H__
#define __LLVM_HEADERS_H__
/** \file blender/blenvm/llvm/llvm_headers.h
* \ingroup llvm
*/
#include "llvm/Analysis/Passes.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/TypeBuilder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Linker/Linker.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/MCJIT.h"
/* passes */
#include "llvm/PassManager.h"
#include "llvm/IR/LegacyPassManagers.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Scalar.h"
#endif /* __LLVM_HEADERS_H__ */

View File

@@ -0,0 +1,365 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/llvm/llvm_modules.cc
* \ingroup llvm
*/
#include <map>
#include <sstream>
#include <stdint.h>
extern "C" {
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
#include "BLI_path_util.h"
#include "BLI_utildefines.h"
#include "BKE_appdir.h"
}
#include "node_graph.h"
#include "llvm_engine.h"
#include "llvm_modules.h"
#include "llvm_headers.h"
#include "llvm_types.h"
#include "util_math.h"
#include "util_opcode.h"
#include "modules.h"
namespace blenvm {
typedef llvm::IRBuilder<> Builder;
void register_extern_node_functions()
{
#define DEF_OPCODE(op) \
{ \
void *func_value = modules::get_node_impl_value<OP_##op>(); \
if (func_value != NULL) \
llvm_register_external_function(bvm_value_function_name(STRINGIFY(op)), func_value); \
void *func_deriv = modules::get_node_impl_deriv<OP_##op>(); \
if (func_deriv != NULL) \
llvm_register_external_function(bvm_deriv_function_name(STRINGIFY(op)), func_value); \
}
BVM_DEFINE_OPCODES
#undef DEF_OPCODE
}
bool llvm_has_external_impl_value(OpCode node_op) {
#define DEF_OPCODE(op) \
if (node_op == OP_##op) \
return modules::get_node_impl_value<OP_##op>() != NULL; \
else
BVM_DEFINE_OPCODES
return false;
#undef DEF_OPCODE
}
bool llvm_has_external_impl_deriv(OpCode node_op) {
#define DEF_OPCODE(op) \
if (node_op == OP_##op) \
return modules::get_node_impl_deriv<OP_##op>() != NULL; \
else
BVM_DEFINE_OPCODES
return false;
#undef DEF_OPCODE
}
static llvm::Value *float_vector_at(Builder &builder, llvm::Value *p_vec, llvm::Value *idx)
{
using namespace llvm;
Type *float_ptr_type = bvm_get_llvm_type(builder.getContext(), BVM_FLOAT, false)->getPointerTo();
Value *p_elem = builder.CreatePointerCast(p_vec, float_ptr_type);
return builder.CreateInBoundsGEP(p_elem, idx);
}
static void def_node_VALUE_t(llvm::LLVMContext &context, llvm::Function *func, const TypeSpec *typespec)
{
using namespace llvm;
bool has_derivs = bvm_type_has_dual_value(typespec);
Function::arg_iterator arg_it = func->arg_begin();
Argument *p_out_val = arg_it++;
Argument *p_out_dx = NULL, *p_out_dy = NULL;
if (has_derivs) {
p_out_dx = arg_it++;
p_out_dy = arg_it++;
}
Argument *in_val = arg_it++;
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
bvm_llvm_copy_value(context, block, p_out_val, in_val, typespec);
if (has_derivs) {
bvm_llvm_set_zero(context, block, p_out_dx, typespec);
bvm_llvm_set_zero(context, block, p_out_dy, typespec);
}
builder.CreateRetVoid();
}
void def_node_VALUE_FLOAT(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_VALUE_t(context, func, TypeSpec::get_typespec("FLOAT"));
}
void def_node_VALUE_INT(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_VALUE_t(context, func, TypeSpec::get_typespec("INT"));
}
void def_node_VALUE_FLOAT3(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_VALUE_t(context, func, TypeSpec::get_typespec("FLOAT3"));
}
void def_node_VALUE_FLOAT4(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_VALUE_t(context, func, TypeSpec::get_typespec("FLOAT4"));
}
void def_node_VALUE_MATRIX44(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_VALUE_t(context, func, TypeSpec::get_typespec("MATRIX44"));
}
void def_node_FLOAT_TO_INT(llvm::LLVMContext &context, llvm::Function *func)
{
using namespace llvm;
Function::arg_iterator arg_it = func->arg_begin();
Argument *p_out_val = arg_it++;
Argument *in_val = arg_it++;
Argument *in_dx = arg_it++;
Argument *in_dy = arg_it++;
UNUSED_VARS(in_dx, in_dy);
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
Type *target_type = bvm_get_llvm_type(context, BVM_INT, false);
Value *ival = builder.CreateFPToSI(in_val, target_type);
builder.CreateStore(ival, p_out_val);
builder.CreateRetVoid();
}
void def_node_INT_TO_FLOAT(llvm::LLVMContext &context, llvm::Function *func)
{
using namespace llvm;
Function::arg_iterator arg_it = func->arg_begin();
Argument *p_out_val = arg_it++;
Argument *p_out_dx = arg_it++;
Argument *p_out_dy = arg_it++;
Argument *in_val = arg_it++;
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
Type *target_type = bvm_get_llvm_type(context, BVM_FLOAT, false);
Value *fval = builder.CreateSIToFP(in_val, target_type);
builder.CreateStore(fval, p_out_val);
Constant *fzero = ConstantFP::get(builder.getContext(), APFloat(0.0f));
builder.CreateStore(fzero, p_out_dx);
builder.CreateStore(fzero, p_out_dy);
builder.CreateRetVoid();
}
static void def_node_SET_FLOATn(llvm::LLVMContext &context, llvm::Function *func, unsigned int n)
{
using namespace llvm;
Function::arg_iterator arg_it = func->arg_begin();
Argument *p_out_val = arg_it++;
Argument *p_out_dx = arg_it++;
Argument *p_out_dy = arg_it++;
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
for (unsigned int i = 0; i < n; ++i) {
Argument *val = arg_it++;
Argument *dx = arg_it++;
Argument *dy = arg_it++;
builder.CreateStore(val, builder.CreateStructGEP(p_out_val, i));
builder.CreateStore(dx, builder.CreateStructGEP(p_out_dx, i));
builder.CreateStore(dy, builder.CreateStructGEP(p_out_dy, i));
}
builder.CreateRetVoid();
}
void def_node_SET_FLOAT3(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_SET_FLOATn(context, func, 3);
}
void def_node_SET_FLOAT4(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_SET_FLOATn(context, func, 4);
}
static void def_node_GET_ELEM_FLOATn(llvm::LLVMContext &context, llvm::Function *func)
{
using namespace llvm;
Function::arg_iterator arg_it = func->arg_begin();
Argument *p_out_val = arg_it++;
Argument *p_out_dx = arg_it++;
Argument *p_out_dy = arg_it++;
Argument *index = arg_it++;
Argument *vec_val = arg_it++;
Argument *vec_dx = arg_it++;
Argument *vec_dy = arg_it++;
BasicBlock *block = BasicBlock::Create(context, "entry", func);
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
builder.CreateStore(builder.CreateLoad(float_vector_at(builder, vec_val, index)), p_out_val);
builder.CreateStore(builder.CreateLoad(float_vector_at(builder, vec_dx, index)), p_out_dx);
builder.CreateStore(builder.CreateLoad(float_vector_at(builder, vec_dy, index)), p_out_dy);
builder.CreateRetVoid();
}
void def_node_GET_ELEM_FLOAT3(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_GET_ELEM_FLOATn(context, func);
}
void def_node_GET_ELEM_FLOAT4(llvm::LLVMContext &context, llvm::Function *func)
{
def_node_GET_ELEM_FLOATn(context, func);
}
/* ------------------------------------------------------------------------- */
static void def_node_GET_DERIVATIVE_t(llvm::LLVMContext &context, llvm::Function *func, const TypeSpec *typespec)
{
using namespace llvm;
ConstantInt* idx0 = ConstantInt::get(context, APInt(32, 0));
ConstantInt* idx1 = ConstantInt::get(context, APInt(32, 1));
Function::arg_iterator arg_it = func->arg_begin();
Argument *out_val = arg_it++;
Argument *out_dx = arg_it++;
Argument *out_dy = arg_it++;
Argument *var = arg_it++;
Argument *in_val = arg_it++;
Argument *in_dx = arg_it++;
Argument *in_dy = arg_it++;
UNUSED_VARS(in_val);
BasicBlock *block = BasicBlock::Create(context, "entry", func);
BasicBlock *block_var0 = BasicBlock::Create(context, "var0", func);
BasicBlock *block_var1 = BasicBlock::Create(context, "var1", func);
BasicBlock *block_end = BasicBlock::Create(context, "end", func);
{
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
/* zero derivatives */
bvm_llvm_set_zero(context, block, out_dx, typespec);
bvm_llvm_set_zero(context, block, out_dy, typespec);
SwitchInst *sw = builder.CreateSwitch(var, block_end, 2);
sw->addCase(idx0, block_var0);
sw->addCase(idx1, block_var1);
}
{
IRBuilder<> builder(context);
builder.SetInsertPoint(block_var0);
bvm_llvm_copy_value(context, block_var0, out_val, in_dx, typespec);
builder.CreateBr(block_end);
}
{
IRBuilder<> builder(context);
builder.SetInsertPoint(block_var1);
bvm_llvm_copy_value(context, block_var1, out_val, in_dy, typespec);
builder.CreateBr(block_end);
}
{
IRBuilder<> builder(context);
builder.SetInsertPoint(block_end);
builder.CreateRetVoid();
}
}
void def_node_GET_DERIVATIVE_FLOAT(llvm::LLVMContext &context, llvm::Function *func)
{
const TypeSpec *typespec = TypeSpec::get_typespec("FLOAT");
return def_node_GET_DERIVATIVE_t(context, func, typespec);
}
void def_node_GET_DERIVATIVE_FLOAT3(llvm::LLVMContext &context, llvm::Function *func)
{
const TypeSpec *typespec = TypeSpec::get_typespec("FLOAT3");
return def_node_GET_DERIVATIVE_t(context, func, typespec);
}
void def_node_GET_DERIVATIVE_FLOAT4(llvm::LLVMContext &context, llvm::Function *func)
{
const TypeSpec *typespec = TypeSpec::get_typespec("FLOAT4");
return def_node_GET_DERIVATIVE_t(context, func, typespec);
}
} /* namespace llvm */

View File

@@ -0,0 +1,75 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_MODULES_H__
#define __LLVM_MODULES_H__
/** \file blender/blenvm/llvm/llvm_modules.h
* \ingroup llvm
*/
#include "util_string.h"
#include "util_opcode.h"
namespace llvm {
class LLVMContext;
class BasicBlock;
class Function;
class Module;
class Value;
}
namespace blenvm {
struct NodeType;
void register_extern_node_functions();
bool llvm_has_external_impl_value(OpCode op);
bool llvm_has_external_impl_deriv(OpCode op);
void def_node_VALUE_FLOAT(llvm::LLVMContext &context, llvm::Function *func);
void def_node_VALUE_INT(llvm::LLVMContext &context, llvm::Function *func);
void def_node_VALUE_FLOAT3(llvm::LLVMContext &context, llvm::Function *func);
void def_node_VALUE_FLOAT4(llvm::LLVMContext &context, llvm::Function *func);
void def_node_VALUE_MATRIX44(llvm::LLVMContext &context, llvm::Function *func);
void def_node_FLOAT_TO_INT(llvm::LLVMContext &context, llvm::Function *func);
void def_node_INT_TO_FLOAT(llvm::LLVMContext &context, llvm::Function *func);
void def_node_SET_FLOAT3(llvm::LLVMContext &context, llvm::Function *func);
void def_node_GET_ELEM_FLOAT3(llvm::LLVMContext &context, llvm::Function *func);
void def_node_SET_FLOAT4(llvm::LLVMContext &context, llvm::Function *func);
void def_node_GET_ELEM_FLOAT4(llvm::LLVMContext &context, llvm::Function *func);
void def_node_GET_DERIVATIVE_FLOAT(llvm::LLVMContext &context, llvm::Function *func);
void def_node_GET_DERIVATIVE_FLOAT3(llvm::LLVMContext &context, llvm::Function *func);
void def_node_GET_DERIVATIVE_FLOAT4(llvm::LLVMContext &context, llvm::Function *func);
} /* namespace blenvm */
#endif /* __LLVM_MODULES_H__ */

View File

@@ -0,0 +1,248 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenvm/llvm/llvm_types.cc
* \ingroup llvm
*/
#include "llvm_headers.h"
#include "llvm_types.h"
namespace blenvm {
llvm::Type *bvm_get_llvm_type(llvm::LLVMContext &context, const TypeSpec *spec, bool use_dual)
{
using namespace llvm;
if (spec->is_structure()) {
const StructSpec *sspec = spec->structure();
std::vector<Type*> fields;
for (int i = 0; i < sspec->num_fields(); ++i) {
Type *ftype = bvm_get_llvm_type(context, sspec->field(i).typespec, use_dual);
fields.push_back(ftype);
}
return StructType::create(context, ArrayRef<Type*>(fields));
}
else {
if (use_dual) {
#define BVM_BUILD_TYPE(t) \
TypeBuilder<BVMTypeLLVMTraits<t>::dual2_type, false>::get(context)
switch (spec->base_type()) {
case BVM_FLOAT: return BVM_BUILD_TYPE(BVM_FLOAT);
case BVM_FLOAT3: return BVM_BUILD_TYPE(BVM_FLOAT3);
case BVM_FLOAT4: return BVM_BUILD_TYPE(BVM_FLOAT4);
case BVM_INT: return BVM_BUILD_TYPE(BVM_INT);
case BVM_MATRIX44: return BVM_BUILD_TYPE(BVM_MATRIX44);
case BVM_STRING: return BVM_BUILD_TYPE(BVM_STRING);
case BVM_RNAPOINTER: return BVM_BUILD_TYPE(BVM_RNAPOINTER);
case BVM_MESH: return BVM_BUILD_TYPE(BVM_MESH);
case BVM_DUPLIS: return BVM_BUILD_TYPE(BVM_DUPLIS);
}
#undef BVM_BUILD_TYPE
}
else {
#define BVM_BUILD_TYPE(t) \
TypeBuilder<BVMTypeLLVMTraits<t>::value_type, false>::get(context)
switch (spec->base_type()) {
case BVM_FLOAT: return BVM_BUILD_TYPE(BVM_FLOAT);
case BVM_FLOAT3: return BVM_BUILD_TYPE(BVM_FLOAT3);
case BVM_FLOAT4: return BVM_BUILD_TYPE(BVM_FLOAT4);
case BVM_INT: return BVM_BUILD_TYPE(BVM_INT);
case BVM_MATRIX44: return BVM_BUILD_TYPE(BVM_MATRIX44);
case BVM_STRING: return BVM_BUILD_TYPE(BVM_STRING);
case BVM_RNAPOINTER: return BVM_BUILD_TYPE(BVM_RNAPOINTER);
case BVM_MESH: return BVM_BUILD_TYPE(BVM_MESH);
case BVM_DUPLIS: return BVM_BUILD_TYPE(BVM_DUPLIS);
}
#undef BVM_BUILD_TYPE
}
}
return NULL;
}
llvm::Type *bvm_get_llvm_type(llvm::LLVMContext &context, BVMType type, bool use_dual)
{
using namespace llvm;
if (use_dual) {
#define BVM_BUILD_TYPE(t) \
TypeBuilder<BVMTypeLLVMTraits<t>::dual2_type, false>::get(context)
switch (type) {
case BVM_FLOAT: return BVM_BUILD_TYPE(BVM_FLOAT);
case BVM_FLOAT3: return BVM_BUILD_TYPE(BVM_FLOAT3);
case BVM_FLOAT4: return BVM_BUILD_TYPE(BVM_FLOAT4);
case BVM_INT: return BVM_BUILD_TYPE(BVM_INT);
case BVM_MATRIX44: return BVM_BUILD_TYPE(BVM_MATRIX44);
case BVM_STRING: return BVM_BUILD_TYPE(BVM_STRING);
case BVM_RNAPOINTER: return BVM_BUILD_TYPE(BVM_RNAPOINTER);
case BVM_MESH: return BVM_BUILD_TYPE(BVM_MESH);
case BVM_DUPLIS: return BVM_BUILD_TYPE(BVM_DUPLIS);
}
#undef BVM_BUILD_TYPE
}
else {
#define BVM_BUILD_TYPE(t) \
TypeBuilder<BVMTypeLLVMTraits<t>::value_type, false>::get(context)
switch (type) {
case BVM_FLOAT: return BVM_BUILD_TYPE(BVM_FLOAT);
case BVM_FLOAT3: return BVM_BUILD_TYPE(BVM_FLOAT3);
case BVM_FLOAT4: return BVM_BUILD_TYPE(BVM_FLOAT4);
case BVM_INT: return BVM_BUILD_TYPE(BVM_INT);
case BVM_MATRIX44: return BVM_BUILD_TYPE(BVM_MATRIX44);
case BVM_STRING: return BVM_BUILD_TYPE(BVM_STRING);
case BVM_RNAPOINTER: return BVM_BUILD_TYPE(BVM_RNAPOINTER);
case BVM_MESH: return BVM_BUILD_TYPE(BVM_MESH);
case BVM_DUPLIS: return BVM_BUILD_TYPE(BVM_DUPLIS);
}
#undef BVM_BUILD_TYPE
}
return NULL;
}
llvm::Constant *bvm_create_llvm_constant(llvm::LLVMContext &context, const NodeConstant *node_value)
{
using namespace llvm;
const TypeSpec *spec = node_value->typedesc().get_typespec();
if (spec->is_structure()) {
// const StructSpec *sspec = spec->structure();
/* TODO don't have value storage for this yet */
return NULL;
}
else {
#define BVM_MAKE_CONSTANT(t) \
{ \
BVMTypeLLVMTraits<t>::value_type c; \
node_value->get(&c); \
return make_constant(context, c); \
} (void)0
switch (spec->base_type()) {
case BVM_FLOAT: BVM_MAKE_CONSTANT(BVM_FLOAT);
case BVM_FLOAT3: BVM_MAKE_CONSTANT(BVM_FLOAT3);
case BVM_FLOAT4: BVM_MAKE_CONSTANT(BVM_FLOAT4);
case BVM_INT: BVM_MAKE_CONSTANT(BVM_INT);
case BVM_MATRIX44: BVM_MAKE_CONSTANT(BVM_MATRIX44);
case BVM_STRING: BVM_MAKE_CONSTANT(BVM_STRING);
case BVM_RNAPOINTER: BVM_MAKE_CONSTANT(BVM_RNAPOINTER);
case BVM_MESH: BVM_MAKE_CONSTANT(BVM_MESH);
case BVM_DUPLIS: BVM_MAKE_CONSTANT(BVM_DUPLIS);
}
#undef BVM_MAKE_CONSTANT
}
return NULL;
}
bool bvm_type_has_dual_value(const TypeSpec *spec)
{
using namespace llvm;
if (spec->is_structure()) {
/* for structs we use invidual dual values for fields */
return false;
}
else {
switch (spec->base_type()) {
case BVM_FLOAT: return BVMTypeLLVMTraits<BVM_FLOAT>::use_dual_value;
case BVM_FLOAT3: return BVMTypeLLVMTraits<BVM_FLOAT3>::use_dual_value;
case BVM_FLOAT4: return BVMTypeLLVMTraits<BVM_FLOAT4>::use_dual_value;
case BVM_INT: return BVMTypeLLVMTraits<BVM_INT>::use_dual_value;
case BVM_MATRIX44: return BVMTypeLLVMTraits<BVM_MATRIX44>::use_dual_value;
case BVM_STRING: return BVMTypeLLVMTraits<BVM_STRING>::use_dual_value;
case BVM_RNAPOINTER: return BVMTypeLLVMTraits<BVM_RNAPOINTER>::use_dual_value;
case BVM_MESH: return BVMTypeLLVMTraits<BVM_MESH>::use_dual_value;
case BVM_DUPLIS: return BVMTypeLLVMTraits<BVM_DUPLIS>::use_dual_value;
}
}
return false;
}
void bvm_llvm_set_zero(llvm::LLVMContext &context, llvm::BasicBlock *block, llvm::Value *ptr, const TypeSpec *spec)
{
using namespace llvm;
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
if (spec->is_structure()) {
/* TODO */
BLI_assert(false);
}
else {
#define BVM_SET_ZERO(t) \
BVMTypeLLVMTraits<t>::set_zero(builder, ptr)
switch (spec->base_type()) {
case BVM_FLOAT: BVM_SET_ZERO(BVM_FLOAT); break;
case BVM_FLOAT3: BVM_SET_ZERO(BVM_FLOAT3); break;
case BVM_FLOAT4: BVM_SET_ZERO(BVM_FLOAT4); break;
case BVM_INT: BVM_SET_ZERO(BVM_INT); break;
case BVM_MATRIX44: BVM_SET_ZERO(BVM_MATRIX44); break;
case BVM_STRING: BVM_SET_ZERO(BVM_STRING); break;
case BVM_RNAPOINTER: BVM_SET_ZERO(BVM_RNAPOINTER); break;
case BVM_MESH: BVM_SET_ZERO(BVM_MESH); break;
case BVM_DUPLIS: BVM_SET_ZERO(BVM_DUPLIS); break;
}
#undef BVM_SET_ZERO
}
}
void bvm_llvm_copy_value(llvm::LLVMContext &context, llvm::BasicBlock *block,
llvm::Value *ptr, llvm::Value *value,
const TypeSpec *spec)
{
using namespace llvm;
IRBuilder<> builder(context);
builder.SetInsertPoint(block);
if (spec->is_structure()) {
/* XXX TODO */
return;
}
else {
#define BVM_COPY_VALUE(t) \
BVMTypeLLVMTraits<t>::copy_value(builder, ptr, value)
switch (spec->base_type()) {
case BVM_FLOAT: BVM_COPY_VALUE(BVM_FLOAT); break;
case BVM_FLOAT3: BVM_COPY_VALUE(BVM_FLOAT3); break;
case BVM_FLOAT4: BVM_COPY_VALUE(BVM_FLOAT4); break;
case BVM_INT: BVM_COPY_VALUE(BVM_INT); break;
case BVM_MATRIX44: BVM_COPY_VALUE(BVM_MATRIX44); break;
case BVM_STRING: BVM_COPY_VALUE(BVM_STRING); break;
case BVM_RNAPOINTER: BVM_COPY_VALUE(BVM_RNAPOINTER); break;
case BVM_MESH: BVM_COPY_VALUE(BVM_MESH); break;
case BVM_DUPLIS: BVM_COPY_VALUE(BVM_DUPLIS); break;
}
#undef BVM_COPY_VALUE
}
}
} /* namespace blenvm */

View File

@@ -0,0 +1,513 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __LLVM_TYPES_H__
#define __LLVM_TYPES_H__
/** \file blender/blenvm/llvm/llvm_types.h
* \ingroup llvm
*/
#include "MEM_guardedalloc.h"
#include "node_value.h"
#include "typedesc.h"
#include "llvm_headers.h"
#include "util_string.h"
namespace llvm {
class LLVMContext;
class Constant;
class FunctionType;
class StructType;
class Type;
class Value;
}
/* TypeBuilder specializations for own structs */
namespace llvm {
template <bool xcompile>
class TypeBuilder<blenvm::float3, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
TypeBuilder<types::ieee_float, xcompile>::get(context),
TypeBuilder<types::ieee_float, xcompile>::get(context),
TypeBuilder<types::ieee_float, xcompile>::get(context),
NULL);
}
enum Fields {
FIELD_X = 0,
FIELD_Y = 1,
FIELD_Z = 2,
};
};
template <bool xcompile>
class TypeBuilder<blenvm::float4, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
TypeBuilder<types::ieee_float, xcompile>::get(context),
TypeBuilder<types::ieee_float, xcompile>::get(context),
TypeBuilder<types::ieee_float, xcompile>::get(context),
TypeBuilder<types::ieee_float, xcompile>::get(context),
NULL);
}
enum Fields {
FIELD_X = 0,
FIELD_Y = 1,
FIELD_Z = 2,
FIELD_W = 3,
};
};
template <bool xcompile>
class TypeBuilder<blenvm::matrix44, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
ArrayType::get(
ArrayType::get(
TypeBuilder<types::ieee_float, xcompile>::get(context),
4),
4),
NULL);
}
};
template <bool xcompile>
class TypeBuilder<PointerRNA, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
TypeBuilder<void*, xcompile>::get(context),
TypeBuilder<void*, xcompile>::get(context),
TypeBuilder<void*, xcompile>::get(context),
NULL);
}
enum Fields {
FIELD_ID = 0,
FIELD_TYPE = 1,
FIELD_DATA = 2,
};
};
template <typename T, bool xcompile>
class TypeBuilder<blenvm::node_counted_ptr<T>, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
TypeBuilder<void*, xcompile>::get(context),
TypeBuilder<size_t*, xcompile>::get(context),
NULL);
}
enum Fields {
FIELD_DATA = 0,
FIELD_REFS = 1,
};
};
template <typename T, bool xcompile>
class TypeBuilder<blenvm::Dual2<T>, xcompile> {
public:
static StructType *get(LLVMContext &context) {
return StructType::get(
TypeBuilder<T, xcompile>::get(context),
TypeBuilder<T, xcompile>::get(context),
TypeBuilder<T, xcompile>::get(context),
NULL);
}
enum Fields {
FIELD_VALUE = 0,
FIELD_DX = 1,
FIELD_DY = 2,
};
};
} /* namespace llvm */
/* ------------------------------------------------------------------------- */
namespace blenvm {
inline llvm::Constant *make_constant(llvm::LLVMContext &context, float f)
{
using namespace llvm;
return ConstantFP::get(context, APFloat(f));
}
inline llvm::Constant *make_constant(llvm::LLVMContext &context, const float3 &f)
{
using namespace llvm;
StructType *stype = TypeBuilder<float3, false>::get(context);
return ConstantStruct::get(stype,
ConstantFP::get(context, APFloat(f.x)),
ConstantFP::get(context, APFloat(f.y)),
ConstantFP::get(context, APFloat(f.z)),
NULL);
}
inline llvm::Constant *make_constant(llvm::LLVMContext &context, const float4 &f)
{
using namespace llvm;
StructType *stype = TypeBuilder<float4, false>::get(context);
return ConstantStruct::get(stype,
ConstantFP::get(context, APFloat(f.x)),
ConstantFP::get(context, APFloat(f.y)),
ConstantFP::get(context, APFloat(f.z)),
ConstantFP::get(context, APFloat(f.w)),
NULL);
}
inline llvm::Constant *make_constant(llvm::LLVMContext &context, int i)
{
using namespace llvm;
return ConstantInt::get(context, APInt(32, i, true));
}
inline llvm::Constant *make_constant(llvm::LLVMContext &context, const matrix44 &m)
{
using namespace llvm;
Type *elem_t = TypeBuilder<float, false>::get(context);
ArrayType *inner_t = ArrayType::get(elem_t, 4);
ArrayType *outer_t = ArrayType::get(inner_t, 4);
StructType *matrix_t = StructType::get(outer_t, NULL);
Constant *constants[4][4];
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
constants[i][j] = ConstantFP::get(context, APFloat(m.data[i][j]));
Constant *cols[4];
for (int i = 0; i < 4; ++i)
cols[i] = ConstantArray::get(inner_t, llvm::ArrayRef<Constant*>(constants[i], 4));
Constant *data = ConstantArray::get(outer_t, llvm::ArrayRef<Constant*>(cols, 4));
return ConstantStruct::get(matrix_t,
data, NULL);
}
inline llvm::Constant *make_constant(llvm::LLVMContext &UNUSED(context), void *UNUSED(p))
{
using namespace llvm;
/* Note: pointer constants are not allowed, should never be used */
BLI_assert(false);
return NULL;
}
inline llvm::Constant *make_constant(llvm::LLVMContext &UNUSED(context), const PointerRNA &UNUSED(p))
{
using namespace llvm;
/* Note: pointer constants are not allowed, should never be used */
BLI_assert(false);
return NULL;
}
template <typename T>
inline llvm::Constant *make_constant(llvm::LLVMContext &context, const Dual2<T> &d)
{
using namespace llvm;
StructType *stype = TypeBuilder<Dual2<T>, false>::get(context);
return ConstantStruct::get(stype,
make_constant(context, d.value()),
make_constant(context, d.dx()),
make_constant(context, d.dy()),
NULL);
}
/* ------------------------------------------------------------------------- */
template <BVMType type>
struct BVMTypeLLVMTraits;
template <>
struct BVMTypeLLVMTraits<BVM_FLOAT> {
enum { use_dual_value = true };
typedef float value_type;
typedef Dual2<value_type> dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
builder.CreateStore(value, ptr);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
Constant *c = ConstantFP::get(builder.getContext(), APFloat(0.0f));
builder.CreateStore(c, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_FLOAT3> {
enum { use_dual_value = true };
typedef float3 value_type;
typedef Dual2<value_type> dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
Value *ivalue = builder.CreateLoad(value);
builder.CreateStore(ivalue, ptr);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
Constant *zero = ConstantAggregateZero::get(TypeBuilder<value_type, false>::get(builder.getContext()));
builder.CreateStore(zero, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_FLOAT4> {
enum { use_dual_value = true };
typedef float4 value_type;
typedef Dual2<value_type> dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
Value *ivalue = builder.CreateLoad(value);
builder.CreateStore(ivalue, ptr);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
Constant *zero = ConstantAggregateZero::get(TypeBuilder<value_type, false>::get(builder.getContext()));
builder.CreateStore(zero, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_INT> {
enum { use_dual_value = false };
typedef int value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
builder.CreateStore(value, ptr);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
Constant *c = builder.getInt32(0);
builder.CreateStore(c, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_MATRIX44> {
enum { use_dual_value = false };
typedef matrix44 value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
Constant *size = builder.getInt32(sizeof(value_type));
builder.CreateMemCpy(ptr, value, size, 0);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
Constant *size = builder.getInt32(sizeof(value_type));
builder.CreateMemSet(ptr, builder.getInt8(0), size, 0);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_STRING> {
enum { use_dual_value = false };
typedef int value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
/* TODO */
BLI_assert(false);
UNUSED_VARS(builder, ptr, value);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
/* TODO */
BLI_assert(false);
UNUSED_VARS(builder, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_RNAPOINTER> {
enum { use_dual_value = false };
typedef PointerRNA value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
Value *src_id = builder.CreateStructGEP(value, 0);
Value *src_type = builder.CreateStructGEP(value, 1);
Value *src_data = builder.CreateStructGEP(value, 2);
Value *dst_id = builder.CreateStructGEP(ptr, 0);
Value *dst_type = builder.CreateStructGEP(ptr, 1);
Value *dst_data = builder.CreateStructGEP(ptr, 2);
builder.CreateStore(builder.CreateLoad(src_id), dst_id);
builder.CreateStore(builder.CreateLoad(src_type), dst_type);
builder.CreateStore(builder.CreateLoad(src_data), dst_data);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
BLI_assert(false);
UNUSED_VARS(builder, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_MESH> {
enum { use_dual_value = false };
typedef struct node_counted_ptr<DerivedMesh> value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
Value *src_data = builder.CreateStructGEP(value, 0);
Value *src_refs = builder.CreateStructGEP(value, 1);
Value *dst_data = builder.CreateStructGEP(ptr, 0);
Value *dst_refs = builder.CreateStructGEP(ptr, 1);
builder.CreateStore(builder.CreateLoad(src_data), dst_data);
builder.CreateStore(builder.CreateLoad(src_refs), dst_refs);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
BLI_assert(false);
UNUSED_VARS(builder, ptr);
}
};
template <>
struct BVMTypeLLVMTraits<BVM_DUPLIS> {
enum { use_dual_value = false };
typedef int value_type;
typedef value_type dual2_type;
template <typename BuilderT>
static void copy_value(BuilderT &builder, llvm::Value *ptr, llvm::Value *value)
{
using namespace llvm;
/* TODO */
BLI_assert(false);
UNUSED_VARS(builder, ptr, value);
}
template <typename BuilderT>
static void set_zero(BuilderT &builder, llvm::Value *ptr)
{
using namespace llvm;
/* TODO */
BLI_assert(false);
UNUSED_VARS(builder, ptr);
}
};
llvm::Type *bvm_get_llvm_type(llvm::LLVMContext &context, const TypeSpec *spec, bool use_dual);
llvm::Type *bvm_get_llvm_type(llvm::LLVMContext &context, BVMType type, bool use_dual);
llvm::Constant *bvm_create_llvm_constant(llvm::LLVMContext &context, const NodeConstant *node_value);
bool bvm_type_has_dual_value(const TypeSpec *spec);
void bvm_llvm_set_zero(llvm::LLVMContext &context, llvm::BasicBlock *block,
llvm::Value *ptr,
const TypeSpec *spec);
void bvm_llvm_copy_value(llvm::LLVMContext &context, llvm::BasicBlock *block,
llvm::Value *ptr, llvm::Value *value,
const TypeSpec *spec);
} /* namespace blenvm */
#endif /* __LLVM_TYPES_H__ */

Some files were not shown because too many files have changed in this diff Show More