Showing preview only (1,200K chars total). Download the full file or copy to clipboard to get everything.
Repository: BlenderNPR/BEER
Branch: Development
Commit: b5ba80553ded
Files: 208
Total size: 1.1 MB
Directory structure:
gitextract_fmgxqm5w/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── config.yml
│ └── workflows/
│ └── BlenderMalt.yml
├── .gitignore
├── BlenderMalt/
│ ├── CBlenderMalt/
│ │ ├── CBlenderMalt.cpp
│ │ ├── CMakeLists.txt
│ │ ├── __init__.py
│ │ ├── build.py
│ │ ├── mikktspace.c
│ │ └── mikktspace.h
│ ├── MaltLights.py
│ ├── MaltMaterial.py
│ ├── MaltMeshes.py
│ ├── MaltNodes/
│ │ ├── MaltCustomPasses.py
│ │ ├── MaltNode.py
│ │ ├── MaltNodeTree.py
│ │ ├── MaltNodeUITools.py
│ │ ├── MaltSocket.py
│ │ ├── Nodes/
│ │ │ ├── MaltArrayIndexNode.py
│ │ │ ├── MaltFunctionNode.py
│ │ │ ├── MaltFunctionSubCategory.py
│ │ │ ├── MaltIONode.py
│ │ │ ├── MaltInlineNode.py
│ │ │ └── MaltStructNode.py
│ │ └── _init_.py
│ ├── MaltPipeline.py
│ ├── MaltProperties.py
│ ├── MaltRenderEngine.py
│ ├── MaltTextures.py
│ ├── MaltUtils.py
│ ├── __init__.py
│ └── readme.md
├── Bridge/
│ ├── Client_API.py
│ ├── Docs.py
│ ├── Material.py
│ ├── Mesh.py
│ ├── Proxys.py
│ ├── Server.py
│ ├── Texture.py
│ ├── __init__.py
│ ├── ipc/
│ │ ├── CMakeLists.txt
│ │ ├── __init__.py
│ │ ├── build.py
│ │ ├── ipc.c
│ │ └── ipc.h
│ └── renderdoc/
│ ├── CMakeLists.txt
│ ├── __init__.py
│ ├── build.py
│ ├── renderdoc_app.h
│ └── renderdoc_wrapper.c
├── LICENSE
├── LICENSE - DEPENDENCIES
├── LICENSE - DEPENDENCIES (FULL TEXT)
├── Malt/
│ ├── GL/
│ │ ├── GL.py
│ │ ├── GLSLEval.py
│ │ ├── GLSLParser/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── build.py
│ │ │ ├── external/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── PEGTL-LICENSE
│ │ │ │ └── rapidjson-LICENSE
│ │ │ └── src/
│ │ │ └── main.cpp
│ │ ├── Mesh.py
│ │ ├── RenderTarget.py
│ │ ├── Shader.py
│ │ ├── Texture.py
│ │ └── readme.md
│ ├── Nodes/
│ │ ├── LineRender.py
│ │ ├── SceneFilter.py
│ │ └── SuperSamplingAA.py
│ ├── Pipeline.py
│ ├── PipelineGraph.py
│ ├── PipelineNode.py
│ ├── PipelineParameters.py
│ ├── PipelinePlugin.py
│ ├── Pipelines/
│ │ ├── MiniPipeline/
│ │ │ └── MiniPipeline.py
│ │ └── NPR_Pipeline/
│ │ ├── Defaults/
│ │ │ └── defaults.blend
│ │ ├── NPR_LightShaders.py
│ │ ├── NPR_Lighting.py
│ │ ├── NPR_Pipeline.py
│ │ ├── Nodes/
│ │ │ ├── Render/
│ │ │ │ ├── RenderLayers.py
│ │ │ │ ├── SceneLighting.py
│ │ │ │ └── ScreenPass.py
│ │ │ └── RenderLayer/
│ │ │ ├── MainPass.py
│ │ │ ├── PrePass.py
│ │ │ └── ScreenPass.py
│ │ └── Shaders/
│ │ ├── NPR_Intellisense.glsl
│ │ ├── NPR_LightShader.glsl
│ │ ├── NPR_MeshShader.glsl
│ │ ├── NPR_Pipeline/
│ │ │ ├── NPR_Filters.glsl
│ │ │ ├── NPR_Lighting.glsl
│ │ │ ├── NPR_Mesh.glsl
│ │ │ ├── NPR_Shading.glsl
│ │ │ └── NPR_Shading2.glsl
│ │ └── NPR_ScreenShader.glsl
│ ├── Render/
│ │ ├── Common.py
│ │ ├── DepthToCompositeDepth.py
│ │ ├── Lighting.py
│ │ ├── Sampling.py
│ │ └── readme.md
│ ├── Scene.py
│ ├── Shaders/
│ │ ├── Common/
│ │ │ ├── Color.glsl
│ │ │ ├── Hash.glsl
│ │ │ ├── Mapping.glsl
│ │ │ ├── Math.glsl
│ │ │ ├── Matrix.glsl
│ │ │ ├── Normal.glsl
│ │ │ ├── Quaternion.glsl
│ │ │ └── Transform.glsl
│ │ ├── Common.glsl
│ │ ├── Filters/
│ │ │ ├── AO.glsl
│ │ │ ├── Bevel.glsl
│ │ │ ├── Blur.glsl
│ │ │ ├── Curvature.glsl
│ │ │ ├── JumpFlood.glsl
│ │ │ ├── Kuwahara.glsl
│ │ │ ├── Line.glsl
│ │ │ ├── Sharpen.glsl
│ │ │ └── StructureTensor.glsl
│ │ ├── Intellisense/
│ │ │ └── intellisense.glsl
│ │ ├── Lighting/
│ │ │ └── Lighting.glsl
│ │ ├── Node Utils/
│ │ │ ├── bool.glsl
│ │ │ ├── common.glsl
│ │ │ ├── float.glsl
│ │ │ ├── int.glsl
│ │ │ ├── node_utils.glsl
│ │ │ ├── packing.glsl
│ │ │ ├── properties.glsl
│ │ │ ├── sampler.glsl
│ │ │ ├── vec2.glsl
│ │ │ ├── vec3.glsl
│ │ │ └── vec4.glsl
│ │ ├── Node Utils 2/
│ │ │ ├── Bool.glsl
│ │ │ ├── Color.glsl
│ │ │ ├── ColorBlend.glsl
│ │ │ ├── Filter.glsl
│ │ │ ├── Float.glsl
│ │ │ ├── Input.glsl
│ │ │ ├── Int.glsl
│ │ │ ├── Mat4.glsl
│ │ │ ├── Parameters.glsl
│ │ │ ├── Texturing.glsl
│ │ │ ├── Vec2.glsl
│ │ │ ├── Vec3.glsl
│ │ │ ├── Vec4.glsl
│ │ │ ├── Vector.glsl
│ │ │ ├── conversion.glsl
│ │ │ └── node_utils_2.glsl
│ │ ├── Passes/
│ │ │ ├── BlendTexture.glsl
│ │ │ ├── BlendTransparency.glsl
│ │ │ ├── CopyTextures.glsl
│ │ │ ├── DepthToBlenderDepth.glsl
│ │ │ ├── JumpFlood.glsl
│ │ │ ├── LineComposite.glsl
│ │ │ ├── Unpack8bitTextures.glsl
│ │ │ └── sRGBConversion.glsl
│ │ ├── Procedural/
│ │ │ ├── Bayer.glsl
│ │ │ ├── Cell_Noise.glsl
│ │ │ ├── Cell_Noise.inl
│ │ │ ├── Fractal_Noise.glsl
│ │ │ ├── Fractal_Noise.inl
│ │ │ ├── Noise.glsl
│ │ │ └── Noise.inl
│ │ ├── SDF/
│ │ │ └── SDF.glsl
│ │ ├── Shading/
│ │ │ ├── BRDF.glsl
│ │ │ └── ShadingModels.glsl
│ │ └── readme.md
│ ├── SourceTranspiler.py
│ ├── Utils.py
│ ├── __init__.py
│ └── readme.md
├── README.md
├── __init__.py
├── docs/
│ ├── Documentation/
│ │ ├── Getting Started.md
│ │ ├── Graphs.md
│ │ ├── Plugins.md
│ │ ├── Settings.md
│ │ └── Tooling.md
│ ├── FAQ.md
│ ├── From-Nodes-To-Code/
│ │ └── From-Nodes-To-Code.md
│ ├── Setup-BlenderMalt-for-Development.md
│ ├── extra/
│ │ ├── extra.css
│ │ └── extra.js
│ ├── index.md
│ ├── overrides/
│ │ └── partials/
│ │ └── _footer.html
│ └── reference/
│ ├── Light-graph.md
│ ├── Mesh-graph.md
│ ├── Render Layer-graph.md
│ ├── Render-graph.md
│ ├── Screen-graph.md
│ └── settings.md
├── mkdocs/
│ ├── mkdocs.yml
│ ├── netlify.toml
│ └── requirements.txt
├── plugins/
│ ├── Experimental/
│ │ ├── RenderLayer/
│ │ │ └── OpaqueLayer.py
│ │ └── __init__.py
│ └── PluginExample/
│ ├── Shaders/
│ │ └── PluginExample.glsl
│ └── __init__.py
└── scripts/
├── PatchDependencies/
│ ├── mcpp-Darwin
│ └── mcpp-Linux
├── build_intellisense_glsl.py
├── format.py
├── generate_conversion_nodes.py
├── generate_license_dependencies_full.py
├── get_glslang.py
├── install_dependencies.py
├── print_pixel_formats.py
├── settings.json
└── setup_blender_addon.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Reports
description: Open a new issue
body:
- type: markdown
attributes:
value: |
Before opening a new issue:
- Read the [readme](https://github.com/bnpr/Malt#malt), check that you meet the minimum requirements and have installed Malt properly.
- Make sure you are running the latest Malt version.
- Search [existing issues](https://github.com/bnpr/Malt/issues) to ensure it has not already been reported.
- type: input
attributes:
label: Malt version
placeholder: Release | Development
validations:
required: true
- type: input
attributes:
label: Blender version
placeholder: Blender 3.0.0
validations:
required: true
- type: input
attributes:
label: OS
placeholder: Windows 10 64 bits
validations:
required: true
- type: input
attributes:
label: Hardware info
placeholder: Intel i7-4790K | 16GB RAM | Nvidia GTX 1060 6GB
validations:
required: true
- type: textarea
attributes:
label: Issue description and reproduction steps
validations:
required: true
- type: textarea
attributes:
label: Attachments
description: |
- The **Log file** from the Blender session where the bug happened.
You can find it in *Preferences > Addons > BlenderMalt > Open Session Log* or search for it in your system temporary folder.
- A zipped **.blend file** with a minimal example to reproduce the error. *(Unless the error is not file dependent)*
> Don't copy/paste the *Log* text. Drag and drop the files to upload them.
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Q&A
url: https://github.com/bnpr/Malt/discussions/categories/q-a
about: Ask your *How to* questions.
- name: Feedback & Feature Requests
url: https://github.com/bnpr/Malt/discussions/categories/feedback-feature-requests
about: Post your feedback and feature requests.
================================================
FILE: .github/workflows/BlenderMalt.yml
================================================
name: BLENDERMALT_PACKAGE_AND_RELEASE
on:
push:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: nelonoel/branch-name@v1.0.1
- name: Rollback Release
uses: author/action-rollback@stable
continue-on-error: true
with:
tag: ${{ env.BRANCH_NAME }}-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.ref_type == 'branch' }}
- uses: ncipollo/release-action@v1
id: create_release
if: ${{ github.ref_type == 'branch' }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
allowUpdates: true
tag: ${{ env.BRANCH_NAME }}-latest
commit: ${{ github.sha }}
prerelease: ${{ env.BRANCH_NAME != 'Release' }}
body: |
[**BlenderMalt-Windows.zip**](https://github.com/${{github.repository}}/releases/download/${{env.BRANCH_NAME}}-latest/BlenderMalt-Windows.zip)
[**BlenderMalt-Linux.zip**](https://github.com/${{github.repository}}/releases/download/${{env.BRANCH_NAME}}-latest/BlenderMalt-Linux.zip)
- name: Rollback Tagged Release
uses: author/action-rollback@stable
continue-on-error: true
with:
tag: ${{ github.action_ref }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.ref_type == 'tag' }}
- uses: ncipollo/release-action@v1
id: create_tagged_release
if: ${{ github.ref_type == 'tag' }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit: ${{ github.sha }}
allowUpdates: true
prerelease: ${{ env.BRANCH_NAME != 'Release' }}
body: |
[**BlenderMalt-Windows.zip**](https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/BlenderMalt-Windows.zip)
[**BlenderMalt-Linux.zip**](https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/BlenderMalt-Linux.zip)
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
tagged_upload_url: ${{ steps.create_tagged_release.outputs.upload_url }}
tagged_release_id: ${{ steps.create_tagged_release.outputs.id }}
package-blender:
needs: [release]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: setup_blender_addon.py
run: python setup_blender_addon.py --copy-modules
working-directory: ${{ github.workspace }}/scripts
- name: Zip Addon
shell: python
run: |
import shutil
shutil.make_archive('BlenderMalt', 'zip', '.', 'BlenderMalt')
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: BlenderMalt-${{ runner.os }}
path: ${{ github.workspace }}/BlenderMalt.zip
- name: Remove Old Files
uses: flcdrg/remove-release-asset-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
release_id: ${{ needs.release.outputs.release_id }}
asset_name: BlenderMalt-${{ runner.os }}.zip
continue-on-error: true
if: ${{ github.ref_type == 'branch' }}
- name: Release Addon
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/BlenderMalt.zip
asset_name: BlenderMalt-${{ runner.os }}.zip
asset_content_type: application/zip
if: ${{ github.ref_type == 'branch' }}
- name: Remove Old Tagged Files
uses: flcdrg/remove-release-asset-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
release_id: ${{ needs.release.outputs.tagged_release_id }}
asset_name: BlenderMalt-${{ runner.os }}.zip
continue-on-error: true
if: ${{ github.ref_type == 'tag' }}
- name: Release Tagged Addon
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.tagged_upload_url }}
asset_path: ${{ github.workspace }}/BlenderMalt.zip
asset_name: BlenderMalt-${{ runner.os }}.zip
asset_content_type: application/zip
if: ${{ github.ref_type == 'tag' }}
================================================
FILE: .gitignore
================================================
.*
!.gitignore
!.gitattributes
!.gitmodules
!.github
__pycache__
*.lib
*.dll
*.so
*.log
================================================
FILE: BlenderMalt/CBlenderMalt/CBlenderMalt.cpp
================================================
#include "stdio.h"
#include "mikktspace.h"
#ifdef _WIN32
#define EXPORT extern "C" __declspec( dllexport )
#else
#define EXPORT extern "C" __attribute__ ((visibility ("default")))
#endif
EXPORT void retrieve_mesh_data(
float* in_positions,
int* in_loop_verts, int loop_count,
int* in_loop_tris,
int* in_loop_tri_polys, int loop_tri_count,
int* in_mat_indices,
float* out_positions, unsigned int** out_indices, unsigned int* out_index_lengths)
{
for(int i = 0; i < loop_count; i++)
{
out_positions[i*3+0] = in_positions[in_loop_verts[i]*3+0];
out_positions[i*3+1] = in_positions[in_loop_verts[i]*3+1];
out_positions[i*3+2] = in_positions[in_loop_verts[i]*3+2];
}
unsigned int* mat_i = out_index_lengths;
for(int i = 0; i < loop_tri_count; i++)
{
int mat = in_mat_indices ? in_mat_indices[in_loop_tri_polys[i]] : 0;
out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+0];
out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+1];
out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+2];
}
}
EXPORT bool mesh_tangents(
int* in_indices, int index_len,
float* in_positions, float* in_normals, float* in_uvs,
float* out_tangents)
{
struct MData
{
int* indices;
int index_len;
float* positions;
float* normals;
float* uvs;
float* tangents;
};
MData data = {
in_indices,
index_len,
in_positions,
in_normals,
in_uvs,
out_tangents,
};
SMikkTSpaceInterface mti = {0};
mti.m_getNumFaces = [](const SMikkTSpaceContext * pContext){
return ((MData*)pContext->m_pUserData)->index_len / 3;
};
mti.m_getNumVerticesOfFace = [](const SMikkTSpaceContext * pContext, const int iFace){
return 3;
};
mti.m_getPosition = [](const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert){
MData* m = (MData*)pContext->m_pUserData;
int i = iFace * 3 + iVert;
fvPosOut[0] = m->positions[m->indices[i] * 3 + 0];
fvPosOut[1] = m->positions[m->indices[i] * 3 + 1];
fvPosOut[2] = m->positions[m->indices[i] * 3 + 2];
};
mti.m_getNormal = [](const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert){
MData* m = (MData*)pContext->m_pUserData;
int i = iFace * 3 + iVert;
fvNormOut[0] = m->normals[m->indices[i] * 3 + 0];
fvNormOut[1] = m->normals[m->indices[i] * 3 + 1];
fvNormOut[2] = m->normals[m->indices[i] * 3 + 2];
};
mti.m_getTexCoord = [](const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert){
MData* m = (MData*)pContext->m_pUserData;
int i = iFace * 3 + iVert;
fvTexcOut[0] = m->uvs[m->indices[i] * 2 + 0];
fvTexcOut[1] = m->uvs[m->indices[i] * 2 + 1];
};
mti.m_setTSpaceBasic = [](const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert){
MData* m = (MData*)pContext->m_pUserData;
int i = iFace * 3 + iVert;
m->tangents[m->indices[i] * 4 + 0] = fvTangent[0];
m->tangents[m->indices[i] * 4 + 1] = fvTangent[1];
m->tangents[m->indices[i] * 4 + 2] = fvTangent[2];
m->tangents[m->indices[i] * 4 + 3] = fSign;
};
SMikkTSpaceContext mtc;
mtc.m_pInterface = &mti;
mtc.m_pUserData = &data;
return genTangSpaceDefault(&mtc);
}
================================================
FILE: BlenderMalt/CBlenderMalt/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.10)
SET(CMAKE_CXX_STANDARD 17)
# set(CMAKE_GENERATOR_PLATFORM x64)
project(CBlenderMalt)
SET(CMAKE_BUILD_TYPE Release)
SET(BUILD_SHARED_LIBS ON)
add_library(CBlenderMalt CBlenderMalt.cpp mikktspace.c)
target_include_directories(CBlenderMalt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/blender_dna)
install(TARGETS CBlenderMalt CONFIGURATIONS Release DESTINATION ${PROJECT_SOURCE_DIR})
================================================
FILE: BlenderMalt/CBlenderMalt/__init__.py
================================================
import subprocess
import os
import ctypes
import platform
src_dir = os.path.abspath(os.path.dirname(__file__))
library = 'libCBlenderMalt.so'
if platform.system() == 'Windows': library = 'CBlenderMalt.dll'
if platform.system() == 'Darwin': library = 'libCBlenderMalt.dylib'
CBlenderMalt = ctypes.CDLL(os.path.join(src_dir, library))
retrieve_mesh_data = CBlenderMalt['retrieve_mesh_data']
retrieve_mesh_data.argtypes = [
ctypes.POINTER(ctypes.c_float),
ctypes.POINTER(ctypes.c_int), ctypes.c_int,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int), ctypes.c_int,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint32)
]
retrieve_mesh_data.restype = None
mesh_tangents = CBlenderMalt['mesh_tangents']
mesh_tangents.argtypes = [
ctypes.POINTER(ctypes.c_int), ctypes.c_int,
ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float),
ctypes.POINTER(ctypes.c_float)
]
mesh_tangents.restype = ctypes.c_bool
================================================
FILE: BlenderMalt/CBlenderMalt/build.py
================================================
import subprocess
import os
import platform
src_dir = os.path.abspath(os.path.dirname(__file__))
build_dir = os.path.join(src_dir, '.build')
try: os.mkdir(build_dir)
except: pass
if platform.system() == 'Windows': #Multi-config generators, like Visual Studio
subprocess.check_call(['cmake', '-A', 'x64', '..'], cwd=build_dir)
subprocess.check_call(['cmake', '--build', '.', '--config', 'Release'], cwd=build_dir)
else: #Single-config generators
subprocess.check_call(['cmake', '..'], cwd=build_dir)
subprocess.check_call(['cmake', '--build', '.'], cwd=build_dir)
subprocess.check_call(['cmake', '--install', '.'], cwd=build_dir)
================================================
FILE: BlenderMalt/CBlenderMalt/mikktspace.c
================================================
/** \file mikktspace/mikktspace.c
* \ingroup mikktspace
*/
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <float.h>
#include <stdlib.h>
#include "mikktspace.h"
#define TFALSE 0
#define TTRUE 1
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#define INTERNAL_RND_SORT_SEED 39871946
// internal structure
typedef struct {
float x, y, z;
} SVec3;
static tbool veq( const SVec3 v1, const SVec3 v2 )
{
return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z);
}
static SVec3 vadd( const SVec3 v1, const SVec3 v2 )
{
SVec3 vRes;
vRes.x = v1.x + v2.x;
vRes.y = v1.y + v2.y;
vRes.z = v1.z + v2.z;
return vRes;
}
static SVec3 vsub( const SVec3 v1, const SVec3 v2 )
{
SVec3 vRes;
vRes.x = v1.x - v2.x;
vRes.y = v1.y - v2.y;
vRes.z = v1.z - v2.z;
return vRes;
}
static SVec3 vscale(const float fS, const SVec3 v)
{
SVec3 vRes;
vRes.x = fS * v.x;
vRes.y = fS * v.y;
vRes.z = fS * v.z;
return vRes;
}
static float LengthSquared( const SVec3 v )
{
return v.x*v.x + v.y*v.y + v.z*v.z;
}
static float Length( const SVec3 v )
{
return sqrtf(LengthSquared(v));
}
static SVec3 Normalize( const SVec3 v )
{
return vscale(1 / Length(v), v);
}
static float vdot( const SVec3 v1, const SVec3 v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}
static tbool NotZero(const float fX)
{
// could possibly use FLT_EPSILON instead
return fabsf(fX) > FLT_MIN;
}
static tbool VNotZero(const SVec3 v)
{
// might change this to an epsilon based test
return NotZero(v.x) || NotZero(v.y) || NotZero(v.z);
}
typedef struct {
int iNrFaces;
int * pTriMembers;
} SSubGroup;
typedef struct {
int iNrFaces;
int * pFaceIndices;
int iVertexRepresentitive;
tbool bOrientPreservering;
} SGroup;
//
#define MARK_DEGENERATE 1
#define QUAD_ONE_DEGEN_TRI 2
#define GROUP_WITH_ANY 4
#define ORIENT_PRESERVING 8
typedef struct {
int FaceNeighbors[3];
SGroup * AssignedGroup[3];
// normalized first order face derivatives
SVec3 vOs, vOt;
float fMagS, fMagT; // original magnitudes
// determines if the current and the next triangle are a quad.
int iOrgFaceNumber;
int iFlag, iTSpacesOffs;
unsigned char vert_num[4];
} STriInfo;
typedef struct {
SVec3 vOs;
float fMagS;
SVec3 vOt;
float fMagT;
int iCounter; // this is to average back into quads.
tbool bOrient;
} STSpace;
static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn);
static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[],
const int iNrActiveGroups, const int piTriListIn[], const float fThresCos,
const SMikkTSpaceContext * pContext);
static int MakeIndex(const int iFace, const int iVert)
{
assert(iVert>=0 && iVert<4 && iFace>=0);
return (iFace<<2) | (iVert&0x3);
}
static void IndexToData(int * piFace, int * piVert, const int iIndexIn)
{
piVert[0] = iIndexIn&0x3;
piFace[0] = iIndexIn>>2;
}
static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1)
{
STSpace ts_res;
// this if is important. Due to floating point precision
// averaging when ts0==ts1 will cause a slight difference
// which results in tangent space splits later on
if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT &&
veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt))
{
ts_res.fMagS = pTS0->fMagS;
ts_res.fMagT = pTS0->fMagT;
ts_res.vOs = pTS0->vOs;
ts_res.vOt = pTS0->vOt;
}
else
{
ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS);
ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT);
ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs);
ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt);
if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs);
if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt);
}
return ts_res;
}
static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index);
static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index);
static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index);
// degen triangles
static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris);
static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris);
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext)
{
return genTangSpace(pContext, 180.0f);
}
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold)
{
// count nr_triangles
int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL;
STriInfo * pTriInfos = NULL;
SGroup * pGroups = NULL;
STSpace * psTspace = NULL;
int iNrTrianglesIn = 0, f=0, t=0, i=0;
int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0;
int iNrActiveGroups = 0, index = 0;
const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext);
tbool bRes = TFALSE;
const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f);
// verify all call-backs have been set
if ( pContext->m_pInterface->m_getNumFaces==NULL ||
pContext->m_pInterface->m_getNumVerticesOfFace==NULL ||
pContext->m_pInterface->m_getPosition==NULL ||
pContext->m_pInterface->m_getNormal==NULL ||
pContext->m_pInterface->m_getTexCoord==NULL )
return TFALSE;
// count triangles on supported faces
for (f=0; f<iNrFaces; f++)
{
const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
if (verts==3) ++iNrTrianglesIn;
else if (verts==4) iNrTrianglesIn += 2;
}
if (iNrTrianglesIn<=0) return TFALSE;
// allocate memory for an index list
piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn);
pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn);
if (piTriListIn==NULL || pTriInfos==NULL)
{
if (piTriListIn!=NULL) free(piTriListIn);
if (pTriInfos!=NULL) free(pTriInfos);
return TFALSE;
}
// make an initial triangle --> face index list
iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
// make a welded index list of identical positions and attributes (pos, norm, texc)
//printf("gen welded index list begin\n");
GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn);
//printf("gen welded index list end\n");
// Mark all degenerate triangles
iTotTris = iNrTrianglesIn;
iDegenTriangles = 0;
for (t=0; t<iTotTris; t++)
{
const int i0 = piTriListIn[t*3+0];
const int i1 = piTriListIn[t*3+1];
const int i2 = piTriListIn[t*3+2];
const SVec3 p0 = GetPosition(pContext, i0);
const SVec3 p1 = GetPosition(pContext, i1);
const SVec3 p2 = GetPosition(pContext, i2);
if (veq(p0,p1) || veq(p0,p2) || veq(p1,p2)) // degenerate
{
pTriInfos[t].iFlag |= MARK_DEGENERATE;
++iDegenTriangles;
}
}
iNrTrianglesIn = iTotTris - iDegenTriangles;
// mark all triangle pairs that belong to a quad with only one
// good triangle. These need special treatment in DegenEpilogue().
// Additionally, move all good triangles to the start of
// pTriInfos[] and piTriListIn[] without changing order and
// put the degenerate triangles last.
DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris);
// evaluate triangle level attributes and neighbor list
//printf("gen neighbors list begin\n");
InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
//printf("gen neighbors list end\n");
// based on the 4 rules, identify groups based on connectivity
iNrMaxGroups = iNrTrianglesIn*3;
pGroups = (SGroup *) malloc(sizeof(SGroup)*iNrMaxGroups);
piGroupTrianglesBuffer = (int *) malloc(sizeof(int)*iNrTrianglesIn*3);
if (pGroups==NULL || piGroupTrianglesBuffer==NULL)
{
if (pGroups!=NULL) free(pGroups);
if (piGroupTrianglesBuffer!=NULL) free(piGroupTrianglesBuffer);
free(piTriListIn);
free(pTriInfos);
return TFALSE;
}
//printf("gen 4rule groups begin\n");
iNrActiveGroups =
Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn);
//printf("gen 4rule groups end\n");
//
psTspace = (STSpace *) malloc(sizeof(STSpace)*iNrTSPaces);
if (psTspace==NULL)
{
free(piTriListIn);
free(pTriInfos);
free(pGroups);
free(piGroupTrianglesBuffer);
return TFALSE;
}
memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces);
for (t=0; t<iNrTSPaces; t++)
{
psTspace[t].vOs.x=1.0f; psTspace[t].vOs.y=0.0f; psTspace[t].vOs.z=0.0f; psTspace[t].fMagS = 1.0f;
psTspace[t].vOt.x=0.0f; psTspace[t].vOt.y=1.0f; psTspace[t].vOt.z=0.0f; psTspace[t].fMagT = 1.0f;
}
// make tspaces, each group is split up into subgroups if necessary
// based on fAngularThreshold. Finally a tangent space is made for
// every resulting subgroup
//printf("gen tspaces begin\n");
bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext);
//printf("gen tspaces end\n");
// clean up
free(pGroups);
free(piGroupTrianglesBuffer);
if (!bRes) // if an allocation in GenerateTSpaces() failed
{
// clean up and return false
free(pTriInfos); free(piTriListIn); free(psTspace);
return TFALSE;
}
// degenerate quads with one good triangle will be fixed by copying a space from
// the good triangle to the coinciding vertex.
// all other degenerate triangles will just copy a space from any good triangle
// with the same welded index in piTriListIn[].
DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris);
free(pTriInfos); free(piTriListIn);
index = 0;
for (f=0; f<iNrFaces; f++)
{
const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
if (verts!=3 && verts!=4) continue;
// I've decided to let degenerate triangles and group-with-anythings
// vary between left/right hand coordinate systems at the vertices.
// All healthy triangles on the other hand are built to always be either or.
/*// force the coordinate system orientation to be uniform for every face.
// (this is already the case for good triangles but not for
// degenerate ones and those with bGroupWithAnything==true)
bool bOrient = psTspace[index].bOrient;
if (psTspace[index].iCounter == 0) // tspace was not derived from a group
{
// look for a space created in GenerateTSpaces() by iCounter>0
bool bNotFound = true;
int i=1;
while (i<verts && bNotFound)
{
if (psTspace[index+i].iCounter > 0) bNotFound=false;
else ++i;
}
if (!bNotFound) bOrient = psTspace[index+i].bOrient;
}*/
// set data
for (i=0; i<verts; i++)
{
const STSpace * pTSpace = &psTspace[index];
float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z};
float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z};
if (pContext->m_pInterface->m_setTSpace!=NULL)
pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i);
if (pContext->m_pInterface->m_setTSpaceBasic!=NULL)
pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i);
++index;
}
}
free(psTspace);
return TTRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct {
float vert[3];
int index;
} STmpVert;
static const int g_iCells = 2048;
#ifdef _MSC_VER
# define NOINLINE __declspec(noinline)
#else
# define NOINLINE __attribute__ ((noinline))
#endif
// it is IMPORTANT that this function is called to evaluate the hash since
// inlining could potentially reorder instructions and generate different
// results for the same effective input value fVal.
static NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal)
{
const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin));
const int iIndex = (int)fIndex;
return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1);
}
static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in);
static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries);
static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
{
// Generate bounding box
int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL;
STmpVert * pTmpVert = NULL;
int i=0, iChannel=0, k=0, e=0;
int iMaxCount=0;
SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim;
float fMin, fMax;
for (i=1; i<(iNrTrianglesIn*3); i++)
{
const int index = piTriList_in_and_out[i];
const SVec3 vP = GetPosition(pContext, index);
if (vMin.x > vP.x) vMin.x = vP.x;
else if (vMax.x < vP.x) vMax.x = vP.x;
if (vMin.y > vP.y) vMin.y = vP.y;
else if (vMax.y < vP.y) vMax.y = vP.y;
if (vMin.z > vP.z) vMin.z = vP.z;
else if (vMax.z < vP.z) vMax.z = vP.z;
}
vDim = vsub(vMax,vMin);
iChannel = 0;
fMin = vMin.x; fMax=vMax.x;
if (vDim.y>vDim.x && vDim.y>vDim.z)
{
iChannel=1;
fMin = vMin.y;
fMax = vMax.y;
}
else if (vDim.z>vDim.x)
{
iChannel=2;
fMin = vMin.z;
fMax = vMax.z;
}
// make allocations
piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3);
piHashCount = (int *) malloc(sizeof(int)*g_iCells);
piHashOffsets = (int *) malloc(sizeof(int)*g_iCells);
piHashCount2 = (int *) malloc(sizeof(int)*g_iCells);
if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL)
{
if (piHashTable!=NULL) free(piHashTable);
if (piHashCount!=NULL) free(piHashCount);
if (piHashOffsets!=NULL) free(piHashOffsets);
if (piHashCount2!=NULL) free(piHashCount2);
GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn);
return;
}
memset(piHashCount, 0, sizeof(int)*g_iCells);
memset(piHashCount2, 0, sizeof(int)*g_iCells);
// count amount of elements in each cell unit
for (i=0; i<(iNrTrianglesIn*3); i++)
{
const int index = piTriList_in_and_out[i];
const SVec3 vP = GetPosition(pContext, index);
const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z);
const int iCell = FindGridCell(fMin, fMax, fVal);
++piHashCount[iCell];
}
// evaluate start index of each cell.
piHashOffsets[0]=0;
for (k=1; k<g_iCells; k++)
piHashOffsets[k]=piHashOffsets[k-1]+piHashCount[k-1];
// insert vertices
for (i=0; i<(iNrTrianglesIn*3); i++)
{
const int index = piTriList_in_and_out[i];
const SVec3 vP = GetPosition(pContext, index);
const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z);
const int iCell = FindGridCell(fMin, fMax, fVal);
int * pTable = NULL;
assert(piHashCount2[iCell]<piHashCount[iCell]);
pTable = &piHashTable[piHashOffsets[iCell]];
pTable[piHashCount2[iCell]] = i; // vertex i has been inserted.
++piHashCount2[iCell];
}
for (k=0; k<g_iCells; k++)
assert(piHashCount2[k] == piHashCount[k]); // verify the count
free(piHashCount2);
// find maximum amount of entries in any hash entry
iMaxCount = piHashCount[0];
for (k=1; k<g_iCells; k++)
if (iMaxCount<piHashCount[k])
iMaxCount=piHashCount[k];
pTmpVert = (STmpVert *) malloc(sizeof(STmpVert)*iMaxCount);
// complete the merge
for (k=0; k<g_iCells; k++)
{
// extract table of cell k and amount of entries in it
int * pTable = &piHashTable[piHashOffsets[k]];
const int iEntries = piHashCount[k];
if (iEntries < 2) continue;
if (pTmpVert!=NULL)
{
for (e=0; e<iEntries; e++)
{
int i = pTable[e];
const SVec3 vP = GetPosition(pContext, piTriList_in_and_out[i]);
pTmpVert[e].vert[0] = vP.x; pTmpVert[e].vert[1] = vP.y;
pTmpVert[e].vert[2] = vP.z; pTmpVert[e].index = i;
}
MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries-1);
}
else
MergeVertsSlow(piTriList_in_and_out, pContext, pTable, iEntries);
}
if (pTmpVert!=NULL) { free(pTmpVert); }
free(piHashTable);
free(piHashCount);
free(piHashOffsets);
}
static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in)
{
// make bbox
int c=0, l=0, channel=0;
float fvMin[3], fvMax[3];
float dx=0, dy=0, dz=0, fSep=0;
for (c=0; c<3; c++)
{ fvMin[c]=pTmpVert[iL_in].vert[c]; fvMax[c]=fvMin[c]; }
for (l=(iL_in+1); l<=iR_in; l++) {
for (c=0; c<3; c++) {
if (fvMin[c]>pTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c];
if (fvMax[c]<pTmpVert[l].vert[c]) fvMax[c]=pTmpVert[l].vert[c];
}
}
dx = fvMax[0]-fvMin[0];
dy = fvMax[1]-fvMin[1];
dz = fvMax[2]-fvMin[2];
channel = 0;
if (dy>dx && dy>dz) channel=1;
else if (dz>dx) channel=2;
fSep = 0.5f*(fvMax[channel]+fvMin[channel]);
// stop if all vertices are NaNs
if (!isfinite(fSep))
return;
// terminate recursion when the separation/average value
// is no longer strictly between fMin and fMax values.
if (fSep>=fvMax[channel] || fSep<=fvMin[channel])
{
// complete the weld
for (l=iL_in; l<=iR_in; l++)
{
int i = pTmpVert[l].index;
const int index = piTriList_in_and_out[i];
const SVec3 vP = GetPosition(pContext, index);
const SVec3 vN = GetNormal(pContext, index);
const SVec3 vT = GetTexCoord(pContext, index);
tbool bNotFound = TTRUE;
int l2=iL_in, i2rec=-1;
while (l2<l && bNotFound)
{
const int i2 = pTmpVert[l2].index;
const int index2 = piTriList_in_and_out[i2];
const SVec3 vP2 = GetPosition(pContext, index2);
const SVec3 vN2 = GetNormal(pContext, index2);
const SVec3 vT2 = GetTexCoord(pContext, index2);
i2rec=i2;
//if (vP==vP2 && vN==vN2 && vT==vT2)
if (vP.x==vP2.x && vP.y==vP2.y && vP.z==vP2.z &&
vN.x==vN2.x && vN.y==vN2.y && vN.z==vN2.z &&
vT.x==vT2.x && vT.y==vT2.y && vT.z==vT2.z)
bNotFound = TFALSE;
else
++l2;
}
// merge if previously found
if (!bNotFound)
piTriList_in_and_out[i] = piTriList_in_and_out[i2rec];
}
}
else
{
int iL=iL_in, iR=iR_in;
assert((iR_in-iL_in)>0); // at least 2 entries
// separate (by fSep) all points between iL_in and iR_in in pTmpVert[]
while (iL < iR)
{
tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE;
while ((!bReadyLeftSwap) && iL<iR)
{
assert(iL>=iL_in && iL<=iR_in);
bReadyLeftSwap = !(pTmpVert[iL].vert[channel]<fSep);
if (!bReadyLeftSwap) ++iL;
}
while ((!bReadyRightSwap) && iL<iR)
{
assert(iR>=iL_in && iR<=iR_in);
bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep;
if (!bReadyRightSwap) --iR;
}
assert( (iL<iR) || !(bReadyLeftSwap && bReadyRightSwap) );
if (bReadyLeftSwap && bReadyRightSwap)
{
const STmpVert sTmp = pTmpVert[iL];
assert(iL<iR);
pTmpVert[iL] = pTmpVert[iR];
pTmpVert[iR] = sTmp;
++iL; --iR;
}
}
assert(iL==(iR+1) || (iL==iR));
if (iL==iR)
{
const tbool bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep;
if (bReadyRightSwap) ++iL;
else --iR;
}
// only need to weld when there is more than 1 instance of the (x,y,z)
if (iL_in < iR)
MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR); // weld all left of fSep
if (iL < iR_in)
MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in); // weld all right of (or equal to) fSep
}
}
static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries)
{
// this can be optimized further using a tree structure or more hashing.
int e=0;
for (e=0; e<iEntries; e++)
{
int i = pTable[e];
const int index = piTriList_in_and_out[i];
const SVec3 vP = GetPosition(pContext, index);
const SVec3 vN = GetNormal(pContext, index);
const SVec3 vT = GetTexCoord(pContext, index);
tbool bNotFound = TTRUE;
int e2=0, i2rec=-1;
while (e2<e && bNotFound)
{
const int i2 = pTable[e2];
const int index2 = piTriList_in_and_out[i2];
const SVec3 vP2 = GetPosition(pContext, index2);
const SVec3 vN2 = GetNormal(pContext, index2);
const SVec3 vT2 = GetTexCoord(pContext, index2);
i2rec = i2;
if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2))
bNotFound = TFALSE;
else
++e2;
}
// merge if previously found
if (!bNotFound)
piTriList_in_and_out[i] = piTriList_in_and_out[i2rec];
}
}
static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
{
int iNumUniqueVerts = 0, t=0, i=0;
for (t=0; t<iNrTrianglesIn; t++)
{
for (i=0; i<3; i++)
{
const int offs = t*3 + i;
const int index = piTriList_in_and_out[offs];
const SVec3 vP = GetPosition(pContext, index);
const SVec3 vN = GetNormal(pContext, index);
const SVec3 vT = GetTexCoord(pContext, index);
tbool bFound = TFALSE;
int t2=0, index2rec=-1;
while (!bFound && t2<=t)
{
int j=0;
while (!bFound && j<3)
{
const int index2 = piTriList_in_and_out[t2*3 + j];
const SVec3 vP2 = GetPosition(pContext, index2);
const SVec3 vN2 = GetNormal(pContext, index2);
const SVec3 vT2 = GetTexCoord(pContext, index2);
if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2))
bFound = TTRUE;
else
++j;
}
if (!bFound) ++t2;
}
assert(bFound);
// if we found our own
if (index2rec == index) { ++iNumUniqueVerts; }
piTriList_in_and_out[offs] = index2rec;
}
}
}
static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
{
int iTSpacesOffs = 0, f=0, t=0;
int iDstTriIndex = 0;
for (f=0; f<pContext->m_pInterface->m_getNumFaces(pContext); f++)
{
const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
if (verts!=3 && verts!=4) continue;
pTriInfos[iDstTriIndex].iOrgFaceNumber = f;
pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs;
if (verts==3)
{
unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num;
pVerts[0]=0; pVerts[1]=1; pVerts[2]=2;
piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0);
piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1);
piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2);
++iDstTriIndex; // next
}
else
{
{
pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f;
pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs;
}
{
// need an order independent way to evaluate
// tspace on quads. This is done by splitting
// along the shortest diagonal.
const int i0 = MakeIndex(f, 0);
const int i1 = MakeIndex(f, 1);
const int i2 = MakeIndex(f, 2);
const int i3 = MakeIndex(f, 3);
const SVec3 T0 = GetTexCoord(pContext, i0);
const SVec3 T1 = GetTexCoord(pContext, i1);
const SVec3 T2 = GetTexCoord(pContext, i2);
const SVec3 T3 = GetTexCoord(pContext, i3);
const float distSQ_02 = LengthSquared(vsub(T2,T0));
const float distSQ_13 = LengthSquared(vsub(T3,T1));
tbool bQuadDiagIs_02;
if (distSQ_02<distSQ_13)
bQuadDiagIs_02 = TTRUE;
else if (distSQ_13<distSQ_02)
bQuadDiagIs_02 = TFALSE;
else
{
const SVec3 P0 = GetPosition(pContext, i0);
const SVec3 P1 = GetPosition(pContext, i1);
const SVec3 P2 = GetPosition(pContext, i2);
const SVec3 P3 = GetPosition(pContext, i3);
const float distSQ_02 = LengthSquared(vsub(P2,P0));
const float distSQ_13 = LengthSquared(vsub(P3,P1));
bQuadDiagIs_02 = distSQ_13<distSQ_02 ? TFALSE : TTRUE;
}
if (bQuadDiagIs_02)
{
{
unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num;
pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=2;
}
piTriList_out[iDstTriIndex*3+0] = i0;
piTriList_out[iDstTriIndex*3+1] = i1;
piTriList_out[iDstTriIndex*3+2] = i2;
++iDstTriIndex; // next
{
unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num;
pVerts_B[0]=0; pVerts_B[1]=2; pVerts_B[2]=3;
}
piTriList_out[iDstTriIndex*3+0] = i0;
piTriList_out[iDstTriIndex*3+1] = i2;
piTriList_out[iDstTriIndex*3+2] = i3;
++iDstTriIndex; // next
}
else
{
{
unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num;
pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=3;
}
piTriList_out[iDstTriIndex*3+0] = i0;
piTriList_out[iDstTriIndex*3+1] = i1;
piTriList_out[iDstTriIndex*3+2] = i3;
++iDstTriIndex; // next
{
unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num;
pVerts_B[0]=1; pVerts_B[1]=2; pVerts_B[2]=3;
}
piTriList_out[iDstTriIndex*3+0] = i1;
piTriList_out[iDstTriIndex*3+1] = i2;
piTriList_out[iDstTriIndex*3+2] = i3;
++iDstTriIndex; // next
}
}
}
iTSpacesOffs += verts;
assert(iDstTriIndex<=iNrTrianglesIn);
}
for (t=0; t<iNrTrianglesIn; t++)
pTriInfos[t].iFlag = 0;
// return total amount of tspaces
return iTSpacesOffs;
}
static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index)
{
int iF, iI;
SVec3 res; float pos[3];
IndexToData(&iF, &iI, index);
pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI);
res.x=pos[0]; res.y=pos[1]; res.z=pos[2];
return res;
}
static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index)
{
int iF, iI;
SVec3 res; float norm[3];
IndexToData(&iF, &iI, index);
pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI);
res.x=norm[0]; res.y=norm[1]; res.z=norm[2];
return res;
}
static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index)
{
int iF, iI;
SVec3 res; float texc[2];
IndexToData(&iF, &iI, index);
pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI);
res.x=texc[0]; res.y=texc[1]; res.z=1.0f;
return res;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
typedef union {
struct
{
int i0, i1, f;
};
int array[3];
} SEdge;
static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn);
static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn);
// returns the texture area times 2
static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[])
{
const SVec3 t1 = GetTexCoord(pContext, indices[0]);
const SVec3 t2 = GetTexCoord(pContext, indices[1]);
const SVec3 t3 = GetTexCoord(pContext, indices[2]);
const float t21x = t2.x-t1.x;
const float t21y = t2.y-t1.y;
const float t31x = t3.x-t1.x;
const float t31y = t3.y-t1.y;
const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x;
return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2;
}
static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
{
int f=0, i=0, t=0;
// pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function.
// generate neighbor info list
for (f=0; f<iNrTrianglesIn; f++)
for (i=0; i<3; i++)
{
pTriInfos[f].FaceNeighbors[i] = -1;
pTriInfos[f].AssignedGroup[i] = NULL;
pTriInfos[f].vOs.x=0.0f; pTriInfos[f].vOs.y=0.0f; pTriInfos[f].vOs.z=0.0f;
pTriInfos[f].vOt.x=0.0f; pTriInfos[f].vOt.y=0.0f; pTriInfos[f].vOt.z=0.0f;
pTriInfos[f].fMagS = 0;
pTriInfos[f].fMagT = 0;
// assumed bad
pTriInfos[f].iFlag |= GROUP_WITH_ANY;
}
// evaluate first order derivatives
for (f=0; f<iNrTrianglesIn; f++)
{
// initial values
const SVec3 v1 = GetPosition(pContext, piTriListIn[f*3+0]);
const SVec3 v2 = GetPosition(pContext, piTriListIn[f*3+1]);
const SVec3 v3 = GetPosition(pContext, piTriListIn[f*3+2]);
const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f*3+0]);
const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f*3+1]);
const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f*3+2]);
const float t21x = t2.x-t1.x;
const float t21y = t2.y-t1.y;
const float t31x = t3.x-t1.x;
const float t31y = t3.y-t1.y;
const SVec3 d1 = vsub(v2,v1);
const SVec3 d2 = vsub(v3,v1);
const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x;
//assert(fSignedAreaSTx2!=0);
SVec3 vOs = vsub(vscale(t31y,d1), vscale(t21y,d2)); // eq 18
SVec3 vOt = vadd(vscale(-t31x,d1), vscale(t21x,d2)); // eq 19
pTriInfos[f].iFlag |= (fSignedAreaSTx2>0 ? ORIENT_PRESERVING : 0);
if ( NotZero(fSignedAreaSTx2) )
{
const float fAbsArea = fabsf(fSignedAreaSTx2);
const float fLenOs = Length(vOs);
const float fLenOt = Length(vOt);
const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f;
if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs);
if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt);
// evaluate magnitudes prior to normalization of vOs and vOt
pTriInfos[f].fMagS = fLenOs / fAbsArea;
pTriInfos[f].fMagT = fLenOt / fAbsArea;
// if this is a good triangle
if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT))
pTriInfos[f].iFlag &= (~GROUP_WITH_ANY);
}
}
// force otherwise healthy quads to a fixed orientation
while (t<(iNrTrianglesIn-1))
{
const int iFO_a = pTriInfos[t].iOrgFaceNumber;
const int iFO_b = pTriInfos[t+1].iOrgFaceNumber;
if (iFO_a==iFO_b) // this is a quad
{
const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
// bad triangles should already have been removed by
// DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false
if ((bIsDeg_a||bIsDeg_b)==TFALSE)
{
const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
// if this happens the quad has extremely bad mapping!!
if (bOrientA!=bOrientB)
{
//printf("found quad with bad mapping\n");
tbool bChooseOrientFirstTri = TFALSE;
if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE;
else if ( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) )
bChooseOrientFirstTri = TTRUE;
// force match
{
const int t0 = bChooseOrientFirstTri ? t : (t+1);
const int t1 = bChooseOrientFirstTri ? (t+1) : t;
pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first
pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit
}
}
}
t += 2;
}
else
++t;
}
// match up edge pairs
{
SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3);
if (pEdges==NULL)
BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn);
else
{
BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn);
free(pEdges);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup);
static void AddTriToGroup(SGroup * pGroup, const int iTriIndex);
static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn)
{
const int iNrMaxGroups = iNrTrianglesIn*3;
int iNrActiveGroups = 0;
int iOffset = 0, f=0, i=0;
(void)iNrMaxGroups; /* quiet warnings in non debug mode */
for (f=0; f<iNrTrianglesIn; f++)
{
for (i=0; i<3; i++)
{
// if not assigned to a group
if ((pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 && pTriInfos[f].AssignedGroup[i]==NULL)
{
tbool bOrPre;
int neigh_indexL, neigh_indexR;
const int vert_index = piTriListIn[f*3+i];
assert(iNrActiveGroups<iNrMaxGroups);
pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups];
pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index;
pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0;
pTriInfos[f].AssignedGroup[i]->iNrFaces = 0;
pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset];
++iNrActiveGroups;
AddTriToGroup(pTriInfos[f].AssignedGroup[i], f);
bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
neigh_indexL = pTriInfos[f].FaceNeighbors[i];
neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2];
if (neigh_indexL>=0) // neighbor
{
const tbool bAnswer =
AssignRecur(piTriListIn, pTriInfos, neigh_indexL,
pTriInfos[f].AssignedGroup[i] );
const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE;
assert(bAnswer || bDiff);
(void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */
}
if (neigh_indexR>=0) // neighbor
{
const tbool bAnswer =
AssignRecur(piTriListIn, pTriInfos, neigh_indexR,
pTriInfos[f].AssignedGroup[i] );
const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE;
assert(bAnswer || bDiff);
(void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */
}
// update offset
iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces;
// since the groups are disjoint a triangle can never
// belong to more than 3 groups. Subsequently something
// is completely screwed if this assertion ever hits.
assert(iOffset <= iNrMaxGroups);
}
}
}
return iNrActiveGroups;
}
static void AddTriToGroup(SGroup * pGroup, const int iTriIndex)
{
pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex;
++pGroup->iNrFaces;
}
static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[],
const int iMyTriIndex, SGroup * pGroup)
{
STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex];
// track down vertex
const int iVertRep = pGroup->iVertexRepresentitive;
const int * pVerts = &piTriListIn[3*iMyTriIndex+0];
int i=-1;
if (pVerts[0]==iVertRep) i=0;
else if (pVerts[1]==iVertRep) i=1;
else if (pVerts[2]==iVertRep) i=2;
assert(i>=0 && i<3);
// early out
if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE;
else if (pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE;
if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0)
{
// first to group with a group-with-anything triangle
// determines it's orientation.
// This is the only existing order dependency in the code!!
if ( pMyTriInfo->AssignedGroup[0] == NULL &&
pMyTriInfo->AssignedGroup[1] == NULL &&
pMyTriInfo->AssignedGroup[2] == NULL )
{
pMyTriInfo->iFlag &= (~ORIENT_PRESERVING);
pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0);
}
}
{
const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
if (bOrient != pGroup->bOrientPreservering) return TFALSE;
}
AddTriToGroup(pGroup, iMyTriIndex);
pMyTriInfo->AssignedGroup[i] = pGroup;
{
const int neigh_indexL = pMyTriInfo->FaceNeighbors[i];
const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2];
if (neigh_indexL>=0)
AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup);
if (neigh_indexR>=0)
AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup);
}
return TTRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2);
static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed);
static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive);
static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[],
const int iNrActiveGroups, const int piTriListIn[], const float fThresCos,
const SMikkTSpaceContext * pContext)
{
STSpace * pSubGroupTspace = NULL;
SSubGroup * pUniSubGroups = NULL;
int * pTmpMembers = NULL;
int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0;
for (g=0; g<iNrActiveGroups; g++)
if (iMaxNrFaces < pGroups[g].iNrFaces)
iMaxNrFaces = pGroups[g].iNrFaces;
if (iMaxNrFaces == 0) return TTRUE;
// make initial allocations
pSubGroupTspace = (STSpace *) malloc(sizeof(STSpace)*iMaxNrFaces);
pUniSubGroups = (SSubGroup *) malloc(sizeof(SSubGroup)*iMaxNrFaces);
pTmpMembers = (int *) malloc(sizeof(int)*iMaxNrFaces);
if (pSubGroupTspace==NULL || pUniSubGroups==NULL || pTmpMembers==NULL)
{
if (pSubGroupTspace!=NULL) free(pSubGroupTspace);
if (pUniSubGroups!=NULL) free(pUniSubGroups);
if (pTmpMembers!=NULL) free(pTmpMembers);
return TFALSE;
}
iUniqueTspaces = 0;
for (g=0; g<iNrActiveGroups; g++)
{
const SGroup * pGroup = &pGroups[g];
int iUniqueSubGroups = 0, s=0;
for (i=0; i<pGroup->iNrFaces; i++) // triangles
{
const int f = pGroup->pFaceIndices[i]; // triangle number
int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0;
SSubGroup tmp_group;
tbool bFound;
SVec3 n, vOs, vOt;
if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0;
else if (pTriInfos[f].AssignedGroup[1]==pGroup) index=1;
else if (pTriInfos[f].AssignedGroup[2]==pGroup) index=2;
assert(index>=0 && index<3);
iVertIndex = piTriListIn[f*3+index];
assert(iVertIndex==pGroup->iVertexRepresentitive);
// is normalized already
n = GetNormal(pContext, iVertIndex);
// project
vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n));
vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n));
if ( VNotZero(vOs) ) vOs = Normalize(vOs);
if ( VNotZero(vOt) ) vOt = Normalize(vOt);
// original face number
iOF_1 = pTriInfos[f].iOrgFaceNumber;
iMembers = 0;
for (j=0; j<pGroup->iNrFaces; j++)
{
const int t = pGroup->pFaceIndices[j]; // triangle number
const int iOF_2 = pTriInfos[t].iOrgFaceNumber;
// project
SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n));
SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n));
if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2);
if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2);
{
const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE;
// make sure triangles which belong to the same quad are joined.
const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE;
const float fCosS = vdot(vOs,vOs2);
const float fCosT = vdot(vOt,vOt2);
assert(f!=t || bSameOrgFace); // sanity check
if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos))
pTmpMembers[iMembers++] = t;
}
}
// sort pTmpMembers
tmp_group.iNrFaces = iMembers;
tmp_group.pTriMembers = pTmpMembers;
if (iMembers>1)
{
unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
QuickSort(pTmpMembers, 0, iMembers-1, uSeed);
}
// look for an existing match
bFound = TFALSE;
l=0;
while (l<iUniqueSubGroups && !bFound)
{
bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]);
if (!bFound) ++l;
}
// assign tangent space index
assert(bFound || l==iUniqueSubGroups);
//piTempTangIndices[f*3+index] = iUniqueTspaces+l;
// if no match was found we allocate a new subgroup
if (!bFound)
{
// insert new subgroup
int * pIndices = (int *) malloc(sizeof(int)*iMembers);
if (pIndices==NULL)
{
// clean up and return false
int s=0;
for (s=0; s<iUniqueSubGroups; s++)
free(pUniSubGroups[s].pTriMembers);
free(pUniSubGroups);
free(pTmpMembers);
free(pSubGroupTspace);
return TFALSE;
}
pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers;
pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices;
memcpy(pIndices, tmp_group.pTriMembers, iMembers*sizeof(int));
pSubGroupTspace[iUniqueSubGroups] =
EvalTspace(tmp_group.pTriMembers, iMembers, piTriListIn, pTriInfos, pContext, pGroup->iVertexRepresentitive);
++iUniqueSubGroups;
}
// output tspace
{
const int iOffs = pTriInfos[f].iTSpacesOffs;
const int iVert = pTriInfos[f].vert_num[index];
STSpace * pTS_out = &psTspace[iOffs+iVert];
assert(pTS_out->iCounter<2);
assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering);
if (pTS_out->iCounter==1)
{
*pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]);
pTS_out->iCounter = 2; // update counter
pTS_out->bOrient = pGroup->bOrientPreservering;
}
else
{
assert(pTS_out->iCounter==0);
*pTS_out = pSubGroupTspace[l];
pTS_out->iCounter = 1; // update counter
pTS_out->bOrient = pGroup->bOrientPreservering;
}
}
}
// clean up and offset iUniqueTspaces
for (s=0; s<iUniqueSubGroups; s++)
free(pUniSubGroups[s].pTriMembers);
iUniqueTspaces += iUniqueSubGroups;
}
// clean up
free(pUniSubGroups);
free(pTmpMembers);
free(pSubGroupTspace);
return TTRUE;
}
static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[],
const SMikkTSpaceContext * pContext, const int iVertexRepresentitive)
{
STSpace res;
float fAngleSum = 0;
int face=0;
res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f;
res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f;
res.fMagS = 0; res.fMagT = 0;
for (face=0; face<iFaces; face++)
{
const int f = face_indices[face];
// only valid triangles get to add their contribution
if ( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 )
{
SVec3 n, vOs, vOt, p0, p1, p2, v1, v2;
float fCos, fAngle, fMagS, fMagT;
int i=-1, index=-1, i0=-1, i1=-1, i2=-1;
if (piTriListIn[3*f+0]==iVertexRepresentitive) i=0;
else if (piTriListIn[3*f+1]==iVertexRepresentitive) i=1;
else if (piTriListIn[3*f+2]==iVertexRepresentitive) i=2;
assert(i>=0 && i<3);
// project
index = piTriListIn[3*f+i];
n = GetNormal(pContext, index);
vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n));
vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n));
if ( VNotZero(vOs) ) vOs = Normalize(vOs);
if ( VNotZero(vOt) ) vOt = Normalize(vOt);
i2 = piTriListIn[3*f + (i<2?(i+1):0)];
i1 = piTriListIn[3*f + i];
i0 = piTriListIn[3*f + (i>0?(i-1):2)];
p0 = GetPosition(pContext, i0);
p1 = GetPosition(pContext, i1);
p2 = GetPosition(pContext, i2);
v1 = vsub(p0,p1);
v2 = vsub(p2,p1);
// project
v1 = vsub(v1, vscale(vdot(n,v1),n)); if ( VNotZero(v1) ) v1 = Normalize(v1);
v2 = vsub(v2, vscale(vdot(n,v2),n)); if ( VNotZero(v2) ) v2 = Normalize(v2);
// weight contribution by the angle
// between the two edge vectors
fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos);
fAngle = (float) acos(fCos);
fMagS = pTriInfos[f].fMagS;
fMagT = pTriInfos[f].fMagT;
res.vOs=vadd(res.vOs, vscale(fAngle,vOs));
res.vOt=vadd(res.vOt,vscale(fAngle,vOt));
res.fMagS+=(fAngle*fMagS);
res.fMagT+=(fAngle*fMagT);
fAngleSum += fAngle;
}
}
// normalize
if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs);
if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt);
if (fAngleSum>0)
{
res.fMagS /= fAngleSum;
res.fMagT /= fAngleSum;
}
return res;
}
static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2)
{
tbool bStillSame=TTRUE;
int i=0;
if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE;
while (i<pg1->iNrFaces && bStillSame)
{
bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE;
if (bStillSame) ++i;
}
return bStillSame;
}
static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed)
{
int iL, iR, n, index, iMid, iTmp;
// Random
unsigned int t=uSeed&31;
t=(uSeed<<t)|(uSeed>>(32-t));
uSeed=uSeed+t+3;
// Random end
iL=iLeft; iR=iRight;
n = (iR-iL)+1;
assert(n>=0);
index = (int) (uSeed%n);
iMid=pSortBuffer[index + iL];
do
{
while (pSortBuffer[iL] < iMid)
++iL;
while (pSortBuffer[iR] > iMid)
--iR;
if (iL <= iR)
{
iTmp = pSortBuffer[iL];
pSortBuffer[iL] = pSortBuffer[iR];
pSortBuffer[iR] = iTmp;
++iL; --iR;
}
}
while (iL <= iR);
if (iLeft < iR)
QuickSort(pSortBuffer, iLeft, iR, uSeed);
if (iL < iRight)
QuickSort(pSortBuffer, iL, iRight, uSeed);
}
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed);
static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in);
static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn)
{
// build array of edges
unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
int iEntries=0, iCurStartIndex=-1, f=0, i=0;
for (f=0; f<iNrTrianglesIn; f++)
for (i=0; i<3; i++)
{
const int i0 = piTriListIn[f*3+i];
const int i1 = piTriListIn[f*3+(i<2?(i+1):0)];
pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1; // put minimum index in i0
pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1
pEdges[f*3+i].f = f; // record face number
}
// sort over all edges by i0, this is the pricy one.
QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed); // sort channel 0 which is i0
// sub sort over i1, should be fast.
// could replace this with a 64 bit int sort over (i0,i1)
// with i0 as msb in the quicksort call above.
iEntries = iNrTrianglesIn*3;
iCurStartIndex = 0;
for (i=1; i<iEntries; i++)
{
if (pEdges[iCurStartIndex].i0 != pEdges[i].i0)
{
const int iL = iCurStartIndex;
const int iR = i-1;
//const int iElems = i-iL;
iCurStartIndex = i;
QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1
}
}
// sub sort over f, which should be fast.
// this step is to remain compliant with BuildNeighborsSlow() when
// more than 2 triangles use the same edge (such as a butterfly topology).
iCurStartIndex = 0;
for (i=1; i<iEntries; i++)
{
if (pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1)
{
const int iL = iCurStartIndex;
const int iR = i-1;
//const int iElems = i-iL;
iCurStartIndex = i;
QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f
}
}
// pair up, adjacent triangles
for (i=0; i<iEntries; i++)
{
const int i0=pEdges[i].i0;
const int i1=pEdges[i].i1;
const int f = pEdges[i].f;
tbool bUnassigned_A;
int i0_A, i1_A;
int edgenum_A, edgenum_B=0; // 0,1 or 2
GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1); // resolve index ordering and edge_num
bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE;
if (bUnassigned_A)
{
// get true index ordering
int j=i+1, t;
tbool bNotFound = TTRUE;
while (j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound)
{
tbool bUnassigned_B;
int i0_B, i1_B;
t = pEdges[j].f;
// flip i0_B and i1_B
GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1); // resolve index ordering and edge_num
//assert(!(i0_A==i1_B && i1_A==i0_B));
bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE;
if (i0_A==i0_B && i1_A==i1_B && bUnassigned_B)
bNotFound = TFALSE;
else
++j;
}
if (!bNotFound)
{
int t = pEdges[j].f;
pTriInfos[f].FaceNeighbors[edgenum_A] = t;
//assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1);
pTriInfos[t].FaceNeighbors[edgenum_B] = f;
}
}
}
}
static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn)
{
int f=0, i=0;
for (f=0; f<iNrTrianglesIn; f++)
{
for (i=0; i<3; i++)
{
// if unassigned
if (pTriInfos[f].FaceNeighbors[i] == -1)
{
const int i0_A = piTriListIn[f*3+i];
const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)];
// search for a neighbor
tbool bFound = TFALSE;
int t=0, j=0;
while (!bFound && t<iNrTrianglesIn)
{
if (t!=f)
{
j=0;
while (!bFound && j<3)
{
// in rev order
const int i1_B = piTriListIn[t*3+j];
const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)];
//assert(!(i0_A==i1_B && i1_A==i0_B));
if (i0_A==i0_B && i1_A==i1_B)
bFound = TTRUE;
else
++j;
}
}
if (!bFound) ++t;
}
// assign neighbors
if (bFound)
{
pTriInfos[f].FaceNeighbors[i] = t;
//assert(pTriInfos[t].FaceNeighbors[j]==-1);
pTriInfos[t].FaceNeighbors[j] = f;
}
}
}
}
}
static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed)
{
unsigned int t;
int iL, iR, n, index, iMid;
// early out
SEdge sTmp;
const int iElems = iRight-iLeft+1;
if (iElems<2) return;
else if (iElems==2)
{
if (pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel])
{
sTmp = pSortBuffer[iLeft];
pSortBuffer[iLeft] = pSortBuffer[iRight];
pSortBuffer[iRight] = sTmp;
}
return;
}
// Random
t=uSeed&31;
t=(uSeed<<t)|(uSeed>>(32-t));
uSeed=uSeed+t+3;
// Random end
iL = iLeft;
iR = iRight;
n = (iR-iL)+1;
assert(n>=0);
index = (int) (uSeed%n);
iMid=pSortBuffer[index + iL].array[channel];
do
{
while (pSortBuffer[iL].array[channel] < iMid)
++iL;
while (pSortBuffer[iR].array[channel] > iMid)
--iR;
if (iL <= iR)
{
sTmp = pSortBuffer[iL];
pSortBuffer[iL] = pSortBuffer[iR];
pSortBuffer[iR] = sTmp;
++iL; --iR;
}
}
while (iL <= iR);
if (iLeft < iR)
QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed);
if (iL < iRight)
QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed);
}
// resolve ordering and edge number
static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in)
{
*edgenum_out = -1;
// test if first index is on the edge
if (indices[0]==i0_in || indices[0]==i1_in)
{
// test if second index is on the edge
if (indices[1]==i0_in || indices[1]==i1_in)
{
edgenum_out[0]=0; // first edge
i0_out[0]=indices[0];
i1_out[0]=indices[1];
}
else
{
edgenum_out[0]=2; // third edge
i0_out[0]=indices[2];
i1_out[0]=indices[0];
}
}
else
{
// only second and third index is on the edge
edgenum_out[0]=1; // second edge
i0_out[0]=indices[1];
i1_out[0]=indices[2];
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// Degenerate triangles ////////////////////////////////////
static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris)
{
int iNextGoodTriangleSearchIndex=-1;
tbool bStillFindingGoodOnes;
// locate quads with only one good triangle
int t=0;
while (t<(iTotTris-1))
{
const int iFO_a = pTriInfos[t].iOrgFaceNumber;
const int iFO_b = pTriInfos[t+1].iOrgFaceNumber;
if (iFO_a==iFO_b) // this is a quad
{
const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
if ((bIsDeg_a^bIsDeg_b)!=0)
{
pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI;
pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI;
}
t += 2;
}
else
++t;
}
// reorder list so all degen triangles are moved to the back
// without reordering the good triangles
iNextGoodTriangleSearchIndex = 1;
t=0;
bStillFindingGoodOnes = TTRUE;
while (t<iNrTrianglesIn && bStillFindingGoodOnes)
{
const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
if (bIsGood)
{
if (iNextGoodTriangleSearchIndex < (t+2))
iNextGoodTriangleSearchIndex = t+2;
}
else
{
int t0, t1;
// search for the first good triangle.
tbool bJustADegenerate = TTRUE;
while (bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris)
{
const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
if (bIsGood) bJustADegenerate=TFALSE;
else ++iNextGoodTriangleSearchIndex;
}
t0 = t;
t1 = iNextGoodTriangleSearchIndex;
++iNextGoodTriangleSearchIndex;
assert(iNextGoodTriangleSearchIndex > (t+1));
// swap triangle t0 and t1
if (!bJustADegenerate)
{
int i=0;
for (i=0; i<3; i++)
{
const int index = piTriList_out[t0*3+i];
piTriList_out[t0*3+i] = piTriList_out[t1*3+i];
piTriList_out[t1*3+i] = index;
}
{
const STriInfo tri_info = pTriInfos[t0];
pTriInfos[t0] = pTriInfos[t1];
pTriInfos[t1] = tri_info;
}
}
else
bStillFindingGoodOnes = TFALSE; // this is not supposed to happen
}
if (bStillFindingGoodOnes) ++t;
}
assert(bStillFindingGoodOnes); // code will still work.
assert(iNrTrianglesIn == t);
}
static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris)
{
int t=0, i=0;
// deal with degenerate triangles
// punishment for degenerate triangles is O(N^2)
for (t=iNrTrianglesIn; t<iTotTris; t++)
{
// degenerate triangles on a quad with one good triangle are skipped
// here but processed in the next loop
const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE;
if (!bSkip)
{
for (i=0; i<3; i++)
{
const int index1 = piTriListIn[t*3+i];
// search through the good triangles
tbool bNotFound = TTRUE;
int j=0;
while (bNotFound && j<(3*iNrTrianglesIn))
{
const int index2 = piTriListIn[j];
if (index1==index2) bNotFound=TFALSE;
else ++j;
}
if (!bNotFound)
{
const int iTri = j/3;
const int iVert = j%3;
const int iSrcVert=pTriInfos[iTri].vert_num[iVert];
const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs;
const int iDstVert=pTriInfos[t].vert_num[i];
const int iDstOffs=pTriInfos[t].iTSpacesOffs;
// copy tspace
psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert];
}
}
}
}
// deal with degenerate quads with one good triangle
for (t=0; t<iNrTrianglesIn; t++)
{
// this triangle belongs to a quad where the
// other triangle is degenerate
if ( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 )
{
SVec3 vDstP;
int iOrgF=-1, i=0;
tbool bNotFound;
unsigned char * pV = pTriInfos[t].vert_num;
int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]);
int iMissingIndex = 0;
if ((iFlag&2)==0) iMissingIndex=1;
else if ((iFlag&4)==0) iMissingIndex=2;
else if ((iFlag&8)==0) iMissingIndex=3;
iOrgF = pTriInfos[t].iOrgFaceNumber;
vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex));
bNotFound = TTRUE;
i=0;
while (bNotFound && i<3)
{
const int iVert = pV[i];
const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert));
if (veq(vSrcP, vDstP)==TTRUE)
{
const int iOffs = pTriInfos[t].iTSpacesOffs;
psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert];
bNotFound=TFALSE;
}
else
++i;
}
assert(!bNotFound);
}
}
}
================================================
FILE: BlenderMalt/CBlenderMalt/mikktspace.h
================================================
/** \file mikktspace/mikktspace.h
* \ingroup mikktspace
*/
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef __MIKKTSPACE_H__
#define __MIKKTSPACE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Author: Morten S. Mikkelsen
* Version: 1.0
*
* The files mikktspace.h and mikktspace.c are designed to be
* stand-alone files and it is important that they are kept this way.
* Not having dependencies on structures/classes/libraries specific
* to the program, in which they are used, allows them to be copied
* and used as is into any tool, program or plugin.
* The code is designed to consistently generate the same
* tangent spaces, for a given mesh, in any tool in which it is used.
* This is done by performing an internal welding step and subsequently an order-independent evaluation
* of tangent space for meshes consisting of triangles and quads.
* This means faces can be received in any order and the same is true for
* the order of vertices of each face. The generated result will not be affected
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
* primitives are present or not will not affect the generated results either.
* Once tangent space calculation is done the vertices of degenerate primitives will simply
* inherit tangent space from neighboring non degenerate primitives.
* The analysis behind this implementation can be found in my master's thesis
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
* by this implementation, the interpolated tangent space is still affected by which diagonal is
* chosen to split each quad. A sensible solution is to have your tools pipeline always
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
* If these have the same length then compare the diagonals defined by the texture coordinates.
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
* and also quad triangulator plugin.
*/
typedef int tbool;
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
typedef struct {
// Returns the number of faces (triangles/quads) on the mesh to be processed.
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
// Returns the number of vertices on face number iFace
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
// either (or both) of the two setTSpace callbacks can be set.
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
// This function is used to return the tangent and fSign to the application.
// fvTangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
// This function is used to return tangent space results to the application.
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
// true magnitudes which can be used for relief mapping effects.
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
} SMikkTSpaceInterface;
struct SMikkTSpaceContext
{
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
};
// these are both thread safe!
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
// normal map sampler must use the exact inverse of the pixel shader transformation.
// The most efficient transformation we can possibly do in the pixel shader is
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
// pixel shader (fast transform out)
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// where vNt is the tangent space normal. The normal map sampler must likewise use the
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
// sampler does (exact inverse of pixel shader):
// float3 row0 = cross(vB, vN);
// float3 row1 = cross(vN, vT);
// float3 row2 = cross(vT, vB);
// float fSign = dot(vT, row0)<0 ? -1 : 1;
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
// where vNout is the sampled normal in some chosen 3D space.
//
// Should you choose to reconstruct the bitangent in the pixel shader instead
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
// However, this must be used both by the sampler and your tools/rendering pipeline.
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: BlenderMalt/MaltLights.py
================================================
import math
import bpy
class MaltLight(bpy.types.PropertyGroup):
def sync_data(self, context):
light = self.id_data
#light.shadow_soft_size = self.radius
if light.type == 'SPOT':
light.cutoff_distance = self.radius
light.spot_size = self.spot_angle
light.spot_blend = self.spot_blend_angle / self.spot_angle
strength : bpy.props.FloatProperty(name='Strength', default=1,
options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, override={'LIBRARY_OVERRIDABLE'})
override_global_settings : bpy.props.BoolProperty(name='Override Global Settings', default=False,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
max_distance : bpy.props.FloatProperty(name='Max Distance', default=100,
options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, override={'LIBRARY_OVERRIDABLE'})
radius : bpy.props.FloatProperty(
name='Radius',
default=5,
update=sync_data,
options={'LIBRARY_EDITABLE', 'ANIMATABLE'},
override={'LIBRARY_OVERRIDABLE'}
)
spot_angle : bpy.props.FloatProperty(
name='Angle',
default=1,
subtype='ANGLE',
min=0,
max=math.pi,
update=sync_data,
options={'LIBRARY_EDITABLE', 'ANIMATABLE'},
override={'LIBRARY_OVERRIDABLE'}
)
spot_blend_angle : bpy.props.FloatProperty(
name='Blend',
default=0.1,
subtype='ANGLE',
min=0,
max=math.pi,
update=sync_data,
options={'LIBRARY_EDITABLE', 'ANIMATABLE'},
override={'LIBRARY_OVERRIDABLE'}
)
def draw_ui(self, layout):
layout.prop(self.id_data, 'color')
layout.prop(self, 'strength')
if self.id_data.type == 'SUN':
layout.prop(self, 'override_global_settings')
if self.override_global_settings:
layout.prop(self, 'max_distance')
if self.id_data.type != 'SUN':
layout.prop(self, 'radius')
if self.id_data.type == 'SPOT':
layout.prop(self, 'spot_angle')
layout.prop(self, 'spot_blend_angle')
classes = [
MaltLight
]
def register():
for _class in classes: bpy.utils.register_class(_class)
bpy.types.Light.malt = bpy.props.PointerProperty(type=MaltLight,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
del bpy.types.Light.malt
================================================
FILE: BlenderMalt/MaltMaterial.py
================================================
import os
import bpy
from BlenderMalt.MaltUtils import malt_path_set_transform, malt_path_get_transform
from . MaltProperties import MaltPropertyGroup
_MATERIALS = {}
class MaltMaterial(bpy.types.PropertyGroup):
def update_source(self, context):
path = self.get_source_path()
if path in _MATERIALS.keys() and _MATERIALS[path]:
self.compiler_error = _MATERIALS[path].compiler_error
self.parameters.setup(_MATERIALS[path].parameters)
else:
self.compiler_error = ''
self.parameters.setup({})
track_shader_changes()
def update_nodes(self, context):
self.parameters.parent = self.shader_nodes
self.update_source(context)
def poll_tree(self, object):
return object.bl_idname == 'MaltTree' and (object.graph_type == self.material_type or self.material_type == '')
material_type : bpy.props.StringProperty(name='Type',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
shader_source : bpy.props.StringProperty(name="Shader Source", subtype='FILE_PATH', update=update_source,
set_transform=malt_path_set_transform, get_transform=malt_path_get_transform,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
shader_nodes : bpy.props.PointerProperty(name="Node Tree", type=bpy.types.NodeTree, update=update_nodes, poll=poll_tree,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
compiler_error : bpy.props.StringProperty(name="Compiler Error",
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
parameters : bpy.props.PointerProperty(type=MaltPropertyGroup, name="Shader Parameters",
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_source_path(self):
path = None
if self.shader_nodes:
if self.shader_nodes.is_active():
path = self.shader_nodes.get_generated_source_path()
else:
path = bpy.path.abspath(self.shader_source, library=self.id_data.library)
if path:
return path
else:
return ''
def draw_ui(self, layout, extension, material_parameters):
layout.active = self.id_data.library is None #only local data can be edited
layout.prop_search(self, 'material_type', bpy.context.scene.world.malt, 'material_types')
row = layout.row()
row.active = self.shader_nodes is None
row.prop(self, 'shader_source')
def nodes_add_or_duplicate():
if self.shader_nodes:
self.shader_nodes = self.shader_nodes.get_copy()
else:
self.shader_nodes = bpy.data.node_groups.new(f'{self.id_data.name} Node Tree', 'MaltTree')
self.shader_nodes.graph_type = self.material_type
self.id_data.update_tag()
self.shader_nodes.update_tag()
row = layout.row(align=True)
row.template_ID(self, "shader_nodes")
if self.shader_nodes:
row.operator('wm.malt_callback', text='', icon='DUPLICATE').callback.set(nodes_add_or_duplicate, 'Duplicate')
from BlenderMalt.MaltNodes.MaltNodeTree import set_node_tree
row.operator('wm.malt_callback', text = '', icon = 'GREASEPENCIL').callback.set(
lambda: set_node_tree(bpy.context, self.shader_nodes)
)
else:
row.operator('wm.malt_callback', text='New', icon='ADD').callback.set(nodes_add_or_duplicate, 'New')
source_path = self.get_source_path()
if source_path != '' and source_path.endswith(extension) == False:
box = layout.box()
box.label(text='Wrong shader extension, should be '+extension+'.', icon='ERROR')
return
if self.compiler_error != '':
layout.operator("wm.malt_print_error", icon='ERROR').message = self.compiler_error
box = layout.box()
lines = self.compiler_error.splitlines()
for line in lines:
box.label(text=line)
material_parameters.draw_ui(layout, filter=extension)
self.parameters.draw_ui(layout)
class MALT_PT_MaterialSettings(bpy.types.Panel):
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "material"
bl_label = "Malt Settings"
COMPAT_ENGINES = {'MALT'}
@classmethod
def poll(cls, context):
return context.scene.render.engine == 'MALT' and context.object is not None
def draw(self, context):
layout = self.layout
ob = context.object
slot = context.material_slot
if ob:
is_sortable = len(ob.material_slots) > 1
rows = 3
if is_sortable:
rows = 5
row = layout.row()
row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
col = row.column(align=True)
col.operator("object.material_slot_add", icon='ADD', text="")
col.operator("object.material_slot_remove", icon='REMOVE', text="")
col.separator()
col.menu("MATERIAL_MT_context_menu", icon='DOWNARROW_HLT', text="")
if is_sortable:
col.separator()
col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
row = layout.row(align=True)
if ob:
row.template_ID(ob, "active_material")
def add_or_duplicate():
nonlocal ob, slot
name = f'{ob.data.name} Material'
material = None
if slot and slot.material:
material = slot.material.copy()
else:
material = bpy.data.materials.new(name)
material.malt.material_type = 'Mesh'
if slot:
slot.material = material
else:
ob.data.materials.append(material)
ob.data.update_tag()
material.update_tag()
if slot and slot.material:
row.operator('wm.malt_callback', text='', icon='DUPLICATE').callback.set(
add_or_duplicate, 'Duplicate')
else:
row.operator('wm.malt_callback', text='New', icon='ADD').callback.set(
add_or_duplicate, 'New')
if slot:
icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
row.prop(slot, "link", icon=icon_link, icon_only=True)
if ob.mode == 'EDIT':
row = layout.row(align=True)
row.operator("object.material_slot_assign", text="Assign")
row.operator("object.material_slot_select", text="Select")
row.operator("object.material_slot_deselect", text="Deselect")
if context.material:
context.material.malt.draw_ui(layout, '.mesh.glsl', context.material.malt_parameters)
classes = (
MaltMaterial,
MALT_PT_MaterialSettings
)
def reset_materials():
global _MATERIALS
_MATERIALS = {}
import time
__TIMESTAMP = time.time()
INITIALIZED = False
def track_shader_changes(force_update=False, async_compilation=True):
from BlenderMalt import MaltPipeline
if MaltPipeline.is_malt_active() == False and force_update == False:
return 1
global INITIALIZED
global __TIMESTAMP
global _MATERIALS
try:
start_time = time.time()
needs_update = []
for material in bpy.data.materials:
path = material.malt.get_source_path()
if path not in needs_update:
if os.path.exists(path):
stats = os.stat(path)
if path not in _MATERIALS.keys() or stats.st_mtime > __TIMESTAMP:
if path not in _MATERIALS:
_MATERIALS[path] = None
needs_update.append(path)
compiled_materials = {}
from . import MaltPipeline
if len(needs_update) > 0:
compiled_materials = MaltPipeline.get_bridge().compile_materials(needs_update, async_compilation=async_compilation)
else:
compiled_materials = MaltPipeline.get_bridge().receive_async_compilation_materials()
if len(compiled_materials) > 0:
for key, value in compiled_materials.items():
_MATERIALS[key] = value
for material in bpy.data.materials:
path = material.malt.get_source_path()
if path in compiled_materials.keys():
material.malt.compiler_error = compiled_materials[path].compiler_error
#TODO: Use parent parameters as defaults for materials with nodes
material.malt.parameters.setup(compiled_materials[path].parameters)
for screen in bpy.data.screens:
for area in screen.areas:
area.tag_redraw()
__TIMESTAMP = start_time
INITIALIZED = True
except:
import traceback
traceback.print_exc()
return 0.1 #Track again in 0.1 second
def register():
for _class in classes: bpy.utils.register_class(_class)
bpy.types.Material.malt = bpy.props.PointerProperty(type=MaltMaterial,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.app.timers.register(track_shader_changes, persistent=True)
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
del bpy.types.Material.malt
bpy.app.timers.unregister(track_shader_changes)
================================================
FILE: BlenderMalt/MaltMeshes.py
================================================
import ctypes
import bpy
MESHES = {}
def get_mesh_name(object):
name = object.name_full
if len(object.modifiers) == 0 and object.data:
name = object.type + '_' + object.data.name_full
return name
def get_mesh(object):
key = get_mesh_name(object)
if key not in MESHES.keys() or MESHES[key] is None:
MESHES[key] = load_mesh(object, key)
return MESHES[key]
def load_mesh(object, name):
from . import CBlenderMalt
m = object.data
if object.type != 'MESH' or object.mode == 'EDIT':
m = object.to_mesh()
if m is None or len(m.polygons) == 0:
return None
m.calc_loop_triangles()
def to_ptr(ptr, ctype):
return (ctype * 1).from_address(ptr)
def attribute_ptr(name, ctype):
ptr = 0
if name in m.attributes:
ptr = m.attributes[name].data[0].as_pointer()
return to_ptr(ptr, ctype)
mesh_ptr = ctypes.c_void_p(m.as_pointer())
loop_count = len(m.loops)
loop_tri_count = len(m.loop_triangles)
material_count = max(1, len(m.materials))
positions = get_load_buffer('positions', ctypes.c_float, (loop_count * 3))
indices = []
indices_ptrs = (ctypes.c_void_p * material_count)()
for i in range(material_count):
indices.append(get_load_buffer('indices'+str(i), ctypes.c_uint32, (loop_tri_count * 3)))
indices_ptrs[i] = ctypes.cast(indices[i].buffer(), ctypes.c_void_p)
indices_lengths = (ctypes.c_uint32 * material_count)()
CBlenderMalt.retrieve_mesh_data(
attribute_ptr("position", ctypes.c_float),
attribute_ptr(".corner_vert", ctypes.c_int), loop_count,
to_ptr(m.loop_triangles[0].as_pointer(), ctypes.c_int),
to_ptr(m.loop_triangle_polygons[0].as_pointer(), ctypes.c_int), loop_tri_count,
attribute_ptr("material_index", ctypes.c_int),
positions.buffer(), indices_ptrs, indices_lengths)
for i in range(material_count):
indices[i]._size = indices_lengths[i]
normals = get_load_buffer('normals', ctypes.c_float, (loop_count * 3))
ctypes.memmove(normals.buffer(), m.corner_normals[0].as_pointer(), normals.size_in_bytes())
uvs_list = []
tangents_buffer = None
for i, uv_layer in enumerate(m.uv_layers):
if i >= 4: break
uvs = (ctypes.c_float * (loop_count * 2)).from_address(uv_layer.data[0].as_pointer())
uv_buffer = get_load_buffer('uv'+str(i), ctypes.c_float, loop_count * 2)
ctypes.memmove(uv_buffer.buffer(), uvs, uv_buffer.size_in_bytes())
uvs_list.append(uv_buffer)
if i == 0 and object.original.data.malt_parameters.bools['precomputed_tangents'].boolean:
tangents_buffer = get_load_buffer('tangents'+str(i), ctypes.c_float, (loop_count * 4))
CBlenderMalt.mesh_tangents(
to_ptr(m.loop_triangles[0].as_pointer(), ctypes.c_int),
loop_tri_count * 3,
positions.buffer(),
normals.buffer(),
uv_buffer.buffer(),
tangents_buffer.buffer())
colors_list = [None]*4
if object.type == 'MESH':
for i in range(4):
override = getattr(object.original.data, f'malt_vertex_color_override_{i}')
attribute = m.color_attributes.get(override)
#if attribute is None and i < len(m.color_attributes):
# attribute = m.color_attributes[i]
if attribute and attribute.domain == 'CORNER':
type = None
if attribute.data_type == 'BYTE_COLOR':
type = ctypes.c_uint8
elif attribute.data_type == 'FLOAT_COLOR':
type = ctypes.c_float
else:
continue
color = (type * (loop_count * 4)).from_address(attribute.data[0].as_pointer())
color_buffer = get_load_buffer('colors'+str(i), type, loop_count*4)
ctypes.memmove(color_buffer.buffer(), color, color_buffer.size_in_bytes())
colors_list[i] = color_buffer
mesh_data = {
'positions': positions,
'indices': indices,
'normals': normals,
'uvs': uvs_list,
'tangents': tangents_buffer,
'colors': colors_list,
}
from . import MaltPipeline
MaltPipeline.get_bridge().load_mesh(name, mesh_data)
from Bridge.Proxys import MeshProxy
return [MeshProxy(name, i) for i in range(material_count)]
def get_load_buffer(name, ctype, size):
from . import MaltPipeline
return MaltPipeline.get_bridge().get_shared_buffer(ctype, size)
def unload_mesh(object):
MESHES[get_mesh_name(object)] = None
def reset_meshes():
global MESHES
MESHES = {}
def draw_vertex_color_overrides(self, context):
if context.scene.render.engine != 'MALT':
return
mesh = context.object.data
self.layout.use_property_split = True
self.layout.label(text='Malt Vertex Colors')
def draw_color_override(key):
value = getattr(mesh, key)
layout = self.layout
if value in mesh.color_attributes and mesh.color_attributes[value].domain != 'CORNER':
layout = self.layout.box()
layout.label(text='Only Face Corner attributes are supported', icon='ERROR')
layout.prop_search(mesh, key, mesh, 'color_attributes')
draw_color_override('malt_vertex_color_override_0')
draw_color_override('malt_vertex_color_override_1')
draw_color_override('malt_vertex_color_override_2')
draw_color_override('malt_vertex_color_override_3')
def register():
bpy.types.Mesh.malt_vertex_color_override_0 = bpy.props.StringProperty(name='0',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Mesh.malt_vertex_color_override_1 = bpy.props.StringProperty(name='1',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Mesh.malt_vertex_color_override_2 = bpy.props.StringProperty(name='2',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Mesh.malt_vertex_color_override_3 = bpy.props.StringProperty(name='3',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.types.DATA_PT_vertex_colors.append(draw_vertex_color_overrides)
def unregister():
bpy.types.DATA_PT_vertex_colors.remove(draw_vertex_color_overrides)
del bpy.types.Mesh.malt_vertex_color_override_0
del bpy.types.Mesh.malt_vertex_color_override_1
del bpy.types.Mesh.malt_vertex_color_override_2
del bpy.types.Mesh.malt_vertex_color_override_3
================================================
FILE: BlenderMalt/MaltNodes/MaltCustomPasses.py
================================================
import bpy
from BlenderMalt import MaltPipeline
class MaltIOParameter(bpy.types.PropertyGroup):
def get_parameter_enums(self, context=None):
types = ['None']
bridge = MaltPipeline.get_bridge()
if bridge and self.graph_type in bridge.graphs:
graph = bridge.graphs[self.graph_type]
if self.io_type in graph.graph_io.keys():
if self.is_output:
types = graph.graph_io[self.io_type].dynamic_output_types
else:
types = graph.graph_io[self.io_type].dynamic_input_types
return [(type, type, type) for type in types]
def get_parameter(self):
try:
return self.get_parameter_enums().index(tuple(self['PARAMETER']))
except:
return 0
def set_parameter(self, value):
self['PARAMETER'] = self.get_parameter_enums()[value]
graph_type : bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
io_type : bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
is_output : bpy.props.BoolProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
parameter : bpy.props.EnumProperty(items=get_parameter_enums, get=get_parameter, set=set_parameter,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def draw(self, context, layout, owner):
layout.label(text='', icon='DOT')
layout.prop(self, 'name', text='')
layout.prop(self, 'parameter', text='')
class MaltCustomIO(bpy.types.PropertyGroup):
inputs : bpy.props.CollectionProperty(type=MaltIOParameter,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
inputs_index : bpy.props.IntProperty()
outputs : bpy.props.CollectionProperty(type=MaltIOParameter,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
outputs_index : bpy.props.IntProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
class MaltCustomPasses(bpy.types.PropertyGroup):
io : bpy.props.CollectionProperty(type=MaltCustomIO,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
class MaltGraphType(bpy.types.PropertyGroup):
custom_passes : bpy.props.CollectionProperty(type=MaltCustomPasses,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
def setup_default_passes(graphs, world=None):
if world is None:
world = bpy.context.scene.world
for name, graph in graphs.items():
if graph.graph_type == graph.SCENE_GRAPH:
if name not in world.malt_graph_types:
world.malt_graph_types.add().name = name
graph_type = world.malt_graph_types[name]
if 'Default' not in graph_type.custom_passes:
graph_type.custom_passes.add().name = 'Default'
for custom_pass in graph_type.custom_passes:
for name, io in graph.graph_io.items():
is_new = False
if name not in custom_pass.io:
custom_pass.io.add().name = name
is_new = True
custom_io = custom_pass.io[name]
if is_new:
def add_io_parameter(name, type, is_output):
parameters = custom_io.outputs if is_output else custom_io.inputs
if name not in parameters:
parameters.add().name = name
parameters[name].graph_type = graph_type.name
parameters[name].io_type = io.name
parameters[name].is_output = is_output
parameters[name].parameter = type
for key, type in io.default_dynamic_inputs.items():
add_io_parameter(key, type, False)
for key, type in io.default_dynamic_outputs.items():
add_io_parameter(key, type, True)
class OT_MaltAddCustomPass(bpy.types.Operator):
bl_idname = "wm.malt_add_custom_pass"
bl_label = "Malt Add Cusstom Pass"
bl_options = {'INTERNAL'}
graph_type : bpy.props.StringProperty()
name : bpy.props.StringProperty(default='Custom Pass')
def draw(self, context):
self.layout.prop(self,'name')
def execute(self, context):
new_pass = context.scene.world.malt_graph_types[self.graph_type].custom_passes.add()
new_pass.name = self.name
for name in MaltPipeline.get_bridge().graphs[self.graph_type].graph_io.keys():
new_pass.io.add().name = name
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
classes = [
MaltIOParameter,
MaltCustomIO,
MaltCustomPasses,
MaltGraphType,
OT_MaltAddCustomPass,
]
def register():
for _class in classes: bpy.utils.register_class(_class)
bpy.types.World.malt_graph_types = bpy.props.CollectionProperty(type=MaltGraphType,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
def unregister():
del bpy.types.World.malt_graph_types
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/MaltNode.py
================================================
from Malt.PipelineParameters import Parameter, Type
import bpy
from BlenderMalt.MaltProperties import MaltPropertyGroup
COLUMN_TYPES = ['Texture','sampler','Material']
class MaltNode():
malt_parameters : bpy.props.PointerProperty(type=MaltPropertyGroup,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
disable_updates : bpy.props.BoolProperty(name="Disable Updates", default=False,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
first_setup : bpy.props.BoolProperty(default=True,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
subscribed : bpy.props.BoolProperty(name="Subscribed", default=False,
options={'SKIP_SAVE','LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
malt_label : bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
#Used on copy() to find the correct node instance
temp_id : bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
internal_name : bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_parameters(self, overrides, resources):
parameters = self.id_data.malt_parameters.get_parameters(overrides, resources)
result = {}
for name, input in self.inputs.items():
if '@' in name or input.active == False:
continue
key = input.get_source_global_reference()
key = key.replace('"','')
if key in parameters:
result[name] = parameters[key]
return result
# Blender will trigger update callbacks even before init and update has finished
# So we use some wrappers to get a more sane behaviour
def _disable_updates_wrapper(self, function):
tree = self.id_data
initial_tree_updates = tree.disable_updates
tree.disable_updates = True
self.disable_updates = True
try:
function()
except:
import traceback
traceback.print_exc()
tree.disable_updates = initial_tree_updates
self.disable_updates = False
def init(self, context):
self._disable_updates_wrapper(self.malt_init)
def setup(self, context=None):
self.setup_implementation()
def setup_implementation(self, copy=None):
self.temp_id = str(hash(self))
if self.internal_name == '':
label = self.malt_label if self.malt_label != '' else self.name
self.internal_name = self.id_data.get_unique_node_id(label)
self._disable_updates_wrapper(lambda: self.malt_setup(copy=copy))
self.first_setup = False
if self.subscribed == False:
def callback(dummy=None):
self.setup_implementation()
bpy.msgbus.subscribe_rna(key=self.path_resolve('name', False), owner=self, args=(None,), notify=callback)
self.subscribed = True
def update(self):
if self.disable_updates:
return
self._disable_updates_wrapper(self.malt_update)
def copy(self, node):
if self.id_data.on_copy:
return
#Find the node from its node tree so we have access to the node id_data
for tree in bpy.data.node_groups:
if node.name in tree.nodes:
if node.temp_id == tree.nodes[node.name].temp_id:
node = tree.nodes[node.name]
break
#We can't copy a node without ID data
if node.id_data is None:
node = None
self.subscribed = False #TODO: Is this needed???
self.internal_name = ''
self.setup_implementation(copy=node)
def free(self):
for input in self.inputs:
key = self.get_input_parameter_name(input.name)
self.id_data.malt_parameters.remove_property(key)
def malt_init(self):
pass
def malt_setup(self, copy=None):
pass
def malt_update(self):
pass
def on_socket_update(self, socket):
pass
def setup_sockets(self, inputs, outputs, expand_structs=True, show_in_material_panel=False, copy=None):
def _expand_structs(sockets):
result = {}
for name, dic in sockets.items():
result[name] = dic
struct_type = self.id_data.get_struct_type(dic['type'])
if struct_type:
for member in struct_type['members']:
result[f"{name}.{member['name']}"] = member
return result
if expand_structs:
inputs = _expand_structs(inputs)
outputs = _expand_structs(outputs)
def setup(current, new):
remove = []
for e in current.keys():
if e not in new:
remove.append(current[e])
for e in remove:
if e.is_linked == False or self.should_delete_outdated_links():
current.remove(e)
else:
e.active = False
socket_index = 0
for i, (name, dic) in enumerate(new.items()):
if '@' in name:
continue #Skip overrides
type = dic['type']
size = dic['size'] if 'size' in dic else 0
is_new_socket = name not in current
if is_new_socket:
current.new('MaltSocket', name)
current[name].show_in_material_panel = show_in_material_panel
if isinstance(type, Parameter):
current[name].data_type = type.type_string()
current[name].array_size = 0 #TODO
else:
current[name].data_type = type
current[name].array_size = size
current[name].active = True
current[name].default_initialization = ''
try:
current[name].ui_label = dic['meta']['label']
except:
current[name].ui_label = name
try:
meta = dic['meta']
if 'default_initialization' in meta:
current[name].default_initialization = meta['default_initialization']
else:
default = meta['default']
if isinstance(default, str):
current[name].default_initialization = default
except:
pass
if is_new_socket:
current[name].setup_shape()
if current.find(name) != socket_index:
current.move(current.find(name), socket_index)
socket_index += 1
setup(self.inputs, inputs)
setup(self.outputs, outputs)
parameters = {}
for name, input in inputs.items():
parameter = None
type = input['type']
size = input['size'] if 'size' in input else 0
try:
subtype = input['meta']['subtype']
except:
subtype = None
if isinstance(type, Parameter):
parameter = type
else:
default_value = input.get('meta', {}).get('default', None)
if size == 0:
try:
parameter = Parameter.from_glsl_type(type, subtype, default_value)
except:
parameter = Parameter(type, Type.OTHER)
else:
parameter = Parameter(type, Type.OTHER)
if parameter:
for k,v in input.get('meta', {}).items():
if k not in parameter.__dict__.keys():
parameter.__dict__[k] = v
label = input.get('meta', {}).get('label', name)
node_label = self.draw_label().replace('.', ' ')
parameter.label = f'{node_label}.{label}'
parameters[self.get_input_parameter_name(name)] = parameter
copy_map = None
copy_map = {}
if copy is None:
#Copy from the node parameters (backward compatibility)
materials = [m for m in bpy.data.materials if m.malt.shader_nodes is self.id_data]
transpiler = self.id_data.get_transpiler()
#Rename old material parameters (backward compatibility)
for key in self.malt_parameters.get_rna().keys():
new_name = self.get_input_parameter_name(key)
if new_name in self.id_data.malt_parameters.get_rna().keys():
continue
copy_map[new_name] = key
old_name = transpiler.global_reference(transpiler.get_source_name(self.name), key)
for material in materials:
if old_name in material.malt.parameters.get_rna().keys():
material.malt.parameters.rename_property(old_name, new_name)
copy = self.malt_parameters
else:
for key in inputs.keys():
from_name = copy.get_input_parameter_name(key)
to_name = self.get_input_parameter_name(key)
copy_map[to_name] = from_name
copy = copy.id_data.malt_parameters
self.id_data.malt_parameters.setup(parameters, skip_private=False, replace_parameters=False,
copy_from=copy, copy_map=copy_map)
for input in self.inputs:
#Sync old nodes with the new system
input.show_in_material_panel_update()
if self.first_setup:
self.setup_width()
def get_input_parameter_name(self, key):
name = key
postfix = ''
if ' @ ' in name:
name_postfix = name.split(' @ ')
name = name_postfix[0]
postfix = ' @ ' + name_postfix[1]
if name in self.inputs.keys() and self.inputs[name].active:
name = self.inputs[name].get_source_global_reference()
name += postfix
name = name.replace('"','')
return name
def should_delete_outdated_links(self):
return False
def calc_node_width(self, point_size) -> float:
import blf
blf.size(0, point_size)
header_padding = 36 # account for a little space for the arrow icon + extra padding on the side of a label
socket_padding = 31 # account for little offset of the text from the node border
def adjust_width(width, text, scale=1, padding=0):
new_width = blf.dimensions(0, text)[0] * scale + padding
result = max(width, new_width)
return result
max_width = adjust_width(0, self.draw_label(), padding=header_padding)
for input in self.inputs.values():
scale = 2
if input.default_initialization or '.' in input.name:
scale = 1
max_width = adjust_width(max_width, input.get_ui_label(), scale=scale, padding=socket_padding*scale)
for output in self.outputs.values():
max_width = adjust_width(max_width, output.get_ui_label(), padding=socket_padding)
return max_width
def setup_width(self):
point_size = bpy.context.preferences.ui_styles[0].widget.points
self.width = self.calc_node_width(point_size)
def get_source_name(self):
return self.id_data.get_transpiler().get_source_name(self.internal_name)
def get_source_code(self, transpiler):
if self.id_data.get_source_language() == 'GLSL':
return '/*{} not implemented*/'.format(self)
elif self.id_data.get_source_language() == 'Python':
return '# {} not implemented'.format(self)
def get_source_socket_reference(self, socket):
if self.id_data.get_source_language() == 'GLSL':
return '/*{} not implemented*/'.format(socket.name)
elif self.id_data.get_source_language() == 'Python':
return '# {} not implemented'.format(socket.name)
def sockets_to_global_parameters(self, sockets, transpiler):
code = ''
for socket in sockets:
if socket.active == False:
continue
if socket.data_type != '' and socket.get_linked() is None and socket.is_struct_member() == False:
code += transpiler.global_declaration(socket.data_type, socket.array_size, socket.get_source_global_reference())
return code
def get_source_global_parameters(self, transpiler):
return self.sockets_to_global_parameters(self.inputs, transpiler)
def setup_socket_shapes(self):
from itertools import chain
for socket in chain(self.inputs.values(), self.outputs.values()):
socket.setup_shape()
def is_column_type(self, data_type):
global COLUMN_TYPES
for type in COLUMN_TYPES:
if type in data_type:
return True
return False
def draw_socket(self, context, layout, socket, text):
draw_parameter = socket.is_output == False and socket.get_linked() is None and socket.default_initialization == ''
if socket.is_struct_member() and (socket.get_struct_socket().get_linked() or socket.get_struct_socket().default_initialization != ''):
draw_parameter = False
if draw_parameter:
column = layout.column()
def get_layout():
if draw_parameter and self.is_column_type(socket.data_type):
return column.column()
else:
return column.row()
parameter_key = socket.get_source_global_reference()
parameter_key = parameter_key.replace('"','')
self.id_data.malt_parameters.draw_parameter(get_layout(), parameter_key, text, is_node_socket=True)
rna = self.id_data.malt_parameters.get_rna()
rna_keys = rna.keys()
for override in ('Preview', 'Final Render'):
key = f'{parameter_key} @ {override}'
if key in rna_keys and rna[key].get('active'):
self.id_data.malt_parameters.draw_parameter(get_layout(), key, None, is_node_socket=True)
else:
layout.label(text=text)
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'MaltTree'
def draw_label(self):
print_label = self.malt_label != ''
if print_label:
for input in self.inputs:
if input.show_in_material_panel:
print_label = False
break
if print_label:
return self.malt_label
else:
return self.name.replace('_', ' ')
classes = []
def register():
for _class in classes: bpy.utils.register_class(_class)
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/MaltNodeTree.py
================================================
import os, time
from Malt.SourceTranspiler import GLSLTranspiler, PythonTranspiler
import bpy
from BlenderMalt.MaltProperties import MaltPropertyGroup
from BlenderMalt import MaltPipeline
from BlenderMalt.MaltUtils import malt_path_set_transform, malt_path_get_transform
from BlenderMalt.MaltNodes.MaltNode import MaltNode
def get_pipeline_graph(context):
if context is None or context.space_data is None or context.space_data.edit_tree is None:
return None
return context.space_data.edit_tree.get_pipeline_graph()
class MaltTree(bpy.types.NodeTree):
bl_label = "Malt Node Tree"
bl_icon = 'NODETREE'
on_copy : bpy.props.BoolProperty(name="On Copy", default=False,
options={'SKIP_SAVE','LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_copy(self):
self.on_copy = True
copy = self.copy()
self.on_copy = False
copy.on_copy = False
copy.subscribed = False
copy.malt_parameters.handle_duplication()
copy.reload_nodes()
copy.update_ext(force_update=True)
return copy
type : bpy.props.EnumProperty(name = 'Type', items = [("MALT", "Malt", "Malt")],
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
@classmethod
def poll(cls, context):
return context.scene.render.engine == 'MALT'
def poll_material(self, material):
return material.malt.shader_nodes is self
def update_graph_type(self, context):
graph = self.get_pipeline_graph()
if graph and graph.default_graph_path and len(self.nodes) == 0:
blend_path, tree_name = graph.default_graph_path
blend_path += '.blend'
if tree_name not in bpy.data.node_groups:
internal_dir = 'NodeTree'
bpy.ops.wm.append(
filepath=os.path.join(blend_path, internal_dir, tree_name),
directory=os.path.join(blend_path, internal_dir),
filename=tree_name
)
bpy.data.node_groups[tree_name].reload_nodes()
name = self.name
copy = bpy.data.node_groups[tree_name]#.get_copy() Workaround crash in 3.4
self.user_remap(copy)
bpy.data.node_groups.remove(self)
copy.name = name
graph_type: bpy.props.StringProperty(name='Type', update=update_graph_type,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
#deprecated
library_source : bpy.props.StringProperty(name="Local Library", subtype='FILE_PATH',
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE'},
set_transform=malt_path_set_transform, get_transform=malt_path_get_transform)
disable_updates : bpy.props.BoolProperty(name="Disable Updates", default=False,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
malt_parameters : bpy.props.PointerProperty(type=MaltPropertyGroup,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
subscribed : bpy.props.BoolProperty(name="Subscribed", default=False,
options={'SKIP_SAVE','LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
links_hash : bpy.props.StringProperty(options={'SKIP_SAVE','LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE'})
def is_active(self):
return self.get_pipeline_graph() is not None
def get_source_language(self):
return self.get_pipeline_graph().language
def get_transpiler(self):
if self.get_source_language() == 'GLSL':
return GLSLTranspiler
elif self.get_source_language() == 'Python':
return PythonTranspiler
def get_library_path(self):
if self.library_source != '':
src_path = bpy.path.abspath(self.library_source, library=self.library)
if os.path.exists(src_path):
return src_path
return None
#deprecated
def get_library(self):
library_path = self.get_library_path()
if library_path:
return get_libraries()[library_path]
else:
return get_empty_library()
def get_full_library(self):
#TODO: Cache
graph = self.get_pipeline_graph()
library = self.get_library()
if library:
result = get_empty_library()
result['functions'].update(graph.functions)
result['structs'].update(graph.structs)
result['subcategories'].update(graph.subcategories)
result['functions'].update(library['functions'])
result['structs'].update(library['structs'])
return result
else:
return {
'functions' : graph.functions,
'structs' : graph.structs,
'subcategories' : graph.subcategories,
}
def get_pipeline_graph(self, graph_type=None):
if graph_type is None:
graph_type = self.graph_type
bridge = MaltPipeline.get_bridge()
if bridge and graph_type in bridge.graphs:
return bridge.graphs[graph_type]
return None
def get_unique_node_id(self, base_name):
base_name = base_name.lower() # Avoid ALL CAPS uniforms, since they are considered private
if 'NODE_NAMES' not in self.keys():
self['NODE_NAMES'] = {}
if base_name not in self['NODE_NAMES'].keys():
self['NODE_NAMES'][base_name] = 1
else:
self['NODE_NAMES'][base_name] += 1
return base_name + str(self['NODE_NAMES'][base_name])
def get_custom_io(self, io_type):
params = []
for node in self.nodes:
if node.bl_idname == 'MaltIONode' and node.io_type == io_type:
io = 'out' if node.is_output else 'in'
for parameter in node.get_custom_parameters():
params.append({
'name': parameter.name,
'type': 'Texture', #TODO
'size': 0,
'io': io,
})
return params
def cast(self, from_type, to_type):
cast_function = f'{to_type}_from_{from_type}'
lib = self.get_full_library()
for function in lib['functions'].values():
if function['name'] == cast_function and len(function['parameters']) == 1:
#TODO: If more than 1 parameter, check if they have default values?
return function['name']
return None
def get_struct_type(self, struct_type):
lib = self.get_full_library()
if struct_type in lib['structs']:
return lib['structs'][struct_type]
return None
def get_generated_source_dir(self):
import os, tempfile
base_path = tempfile.gettempdir()
if bpy.context.blend_data.is_saved:
base_path = bpy.path.abspath('//')
return os.path.join(base_path,'.malt-autogenerated')
def get_generated_source_path(self):
import os
file_prefix = 'temp'
if self.library:
file_prefix = bpy.path.basename(self.library.filepath).split('.')[0]
elif bpy.context.blend_data.is_saved:
file_prefix = bpy.path.basename(bpy.context.blend_data.filepath).split('.')[0]
pipeline_graph = self.get_pipeline_graph()
if pipeline_graph:
return os.path.join(self.get_generated_source_dir(),'{}-{}{}'.format(file_prefix, self.name, pipeline_graph.file_extension))
return None
def get_generated_source(self, force_update=False):
if force_update == False and self.get('source'):
return self['source']
output_nodes = []
linked_nodes = []
pipeline_graph = self.get_pipeline_graph()
if pipeline_graph:
for node in self.nodes:
#TODO: MaltNode.is_result()
if node.bl_idname == 'MaltIONode' and node.is_output:
output_nodes.append(node)
linked_nodes.append(node)
def add_node_inputs(node, list, io_type):
for input in node.inputs:
if input.get_linked():
new_node = input.get_linked().node
if new_node.bl_idname == 'MaltIONode' and new_node.io_type != io_type:
input.links[0].is_muted = True
continue
if new_node not in list:
add_node_inputs(new_node, list, io_type)
list.append(new_node)
if new_node not in linked_nodes:
linked_nodes.append(new_node)
transpiler = self.get_transpiler()
def get_source(output):
nodes = []
add_node_inputs(output, nodes, output.io_type)
code = ''
for node in nodes:
if hasattr(node, 'get_source_code'):
code += node.get_source_code(transpiler) + '\n'
code += output.get_source_code(transpiler)
return code
shader ={}
for output in output_nodes:
shader[output.io_type] = get_source(output)
shader['GLOBAL'] = ''
library_path = self.get_library_path()
if library_path:
shader['GLOBAL'] += '#include "{}"\n'.format(library_path)
for node in linked_nodes:
if hasattr(node, 'get_source_global_parameters'):
shader['GLOBAL'] += node.get_source_global_parameters(transpiler)
self['source'] = pipeline_graph.generate_source(shader)
return self['source']
def reload_nodes(self):
self.disable_updates = True
try:
for node in self.nodes:
if hasattr(node, 'setup'):
node.setup()
for node in self.nodes:
if hasattr(node, 'update'):
node.update()
except:
import traceback
traceback.print_exc()
self.disable_updates = False
def update(self):
if self.is_active():
self.update_ext()
def update_ext(self, force_track_shader_changes=True, force_update=False):
if self.disable_updates:
return
if self.get_pipeline_graph() is None:
return
if self.subscribed == False:
bpy.msgbus.subscribe_rna(key=self.path_resolve('name', False),
owner=self, args=(None,), notify=lambda _ : self.update_ext(force_update=True))
self.subscribed = True
links_str = ''
for link in self.links:
try:
b = link.to_socket
a = b.get_linked(ignore_muted=False)
links_str += str(a) + str(b)
except:
pass #Reroute Node
links_hash = str(hash(links_str))
if force_update == False and links_hash == self.links_hash:
return
self.links_hash = links_hash
self.disable_updates = True
try:
for link in self.links:
try:
b = link.to_socket
a = b.get_linked(ignore_muted=False)
if (a.array_size != b.array_size or
(a.data_type != b.data_type and
self.cast(a.data_type, b.data_type) is None)):
link.is_muted = True
else:
link.is_muted = False
except:
pass
source = self.get_generated_source(force_update=True)
source_dir = self.get_generated_source_dir()
source_path = self.get_generated_source_path()
import pathlib
pathlib.Path(source_dir).mkdir(parents=True, exist_ok=True)
with open(source_path,'w') as f:
f.write(source)
if force_track_shader_changes:
from BlenderMalt import MaltMaterial
MaltMaterial.track_shader_changes()
except:
import traceback
traceback.print_exc()
self.disable_updates = False
# Force a depsgraph update.
# Otherwise these will be outddated in scene_eval
self.update_tag()
def setup_node_trees():
graphs = MaltPipeline.get_bridge().graphs
for name, graph in graphs.items():
preload_menus(graph.structs, graph.functions, graph)
track_library_changes(force_update=True, is_initial_setup=True)
for tree in bpy.data.node_groups:
if tree.bl_idname == 'MaltTree' and tree.is_active():
tree.reload_nodes()
tree.update_ext(force_track_shader_changes=False, force_update=True)
from BlenderMalt import MaltMaterial
MaltMaterial.track_shader_changes()
#SKIP_SAVE doesn't work
def manual_skip_save():
for tree in bpy.data.node_groups:
if tree.bl_idname == 'MaltTree':
tree.subscribed = False
tree.links_hash = ''
for node in tree.nodes:
if hasattr(node, 'subscribed'):
node.subscribed = False
__LIBRARIES = {}
def get_libraries():
return __LIBRARIES
def get_empty_library():
return {
'structs':{},
'functions':{},
'subcategories':{},
'paths':[],
}
__TIMESTAMP = time.time()
def track_library_changes(force_update=False, is_initial_setup=False):
from BlenderMalt import MaltPipeline
if MaltPipeline.is_malt_active() == False and force_update == False:
return 1
bridge = MaltPipeline.get_bridge()
graphs = MaltPipeline.get_bridge().graphs
updated_graphs = []
if is_initial_setup == False:
for name, graph in graphs.items():
if graph.needs_reload():
updated_graphs.append(name)
if len(updated_graphs) > 0:
bridge.reload_graphs(updated_graphs)
for graph_name in updated_graphs:
graph = graphs[graph_name]
preload_menus(graph.structs, graph.functions, graphs[graph_name])
global __LIBRARIES
global __TIMESTAMP
start_time = time.time()
#purge unused libraries
new_dic = {}
for tree in bpy.data.node_groups:
if tree.bl_idname == 'MaltTree' and tree.is_active():
src_path = tree.get_library_path()
if src_path:
if src_path in __LIBRARIES:
new_dic[src_path] = __LIBRARIES[src_path]
else:
new_dic[src_path] = None
__LIBRARIES = new_dic
needs_update = set()
for path, library in __LIBRARIES.items():
root_dir = os.path.dirname(path)
if os.path.exists(path):
if library is None:
needs_update.add(path)
else:
for sub_path in library['paths']:
sub_path = os.path.join(root_dir, sub_path)
if os.path.exists(sub_path):
# Don't track individual files granularly since macros can completely change them
if os.stat(sub_path).st_mtime > __TIMESTAMP:
needs_update.add(path)
break
if len(needs_update) > 0:
results = MaltPipeline.get_bridge().reflect_source_libraries(needs_update)
for path, reflection in results.items():
__LIBRARIES[path] = reflection
preload_menus(reflection['structs'], reflection['functions'])
if is_initial_setup == False and max(len(needs_update), len(updated_graphs)) > 0:
for tree in bpy.data.node_groups:
if tree.bl_idname == 'MaltTree' and tree.is_active():
src_path = tree.get_library_path()
if tree.graph_type in updated_graphs or (src_path and src_path in needs_update):
tree.reload_nodes()
tree.update_ext(force_track_shader_changes=False, force_update=True)
from BlenderMalt import MaltMaterial
MaltMaterial.track_shader_changes()
__TIMESTAMP = start_time
return 0.1
class NODE_PT_MaltNodeTree(bpy.types.Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Malt Nodes"
bl_label = "Malt Node Tree UI"
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'MaltTree'
def draw(self, context):
layout = self.layout
#layout.prop(context.space_data.node_tree, 'generated_source')
def preload_menus(structs, functions, graph=None):
if graph is None:
return
from nodeitems_utils import NodeCategory, NodeItem, register_node_categories, unregister_node_categories
from collections import OrderedDict
# Uses copied code from the <nodeitems_utils> module. Manual check for updates required.
class MaltNodeItem(NodeItem):
def __init__(self, nodetype, category, *, label=None, settings=None, poll=None, draw=None, item_params=None):
if settings is None:
settings = {}
self.nodetype = nodetype
self._label = f'{category} - {label}'
self.button_label = label
self.settings = settings
self.poll = poll
self.item_params = item_params
def draw_default(self, layout, _context):
props = layout.operator("node.add_node", text=self.button_label, text_ctxt=self.translation_context)
props.type = self.nodetype
props.use_transform = True
for setting in self.settings.items():
ops = props.settings.add()
ops.name = setting[0]
ops.value = setting[1]
self.draw = staticmethod(draw) if draw else staticmethod(draw_default)
class MaltSearchMenuItem(MaltNodeItem):
def __init__(self, nodetype, category, *, label=None, settings=None, poll=None):
def draw_nothing(self, layout, _context):
return
super().__init__(nodetype, category, label=label, settings=settings, poll=poll, draw=draw_nothing)
category_id = f'BLENDERMALT_{graph.name.upper()}'
try:
unregister_node_categories(category_id) # you could also check the hidden <nodeitems_utils._node_categories>
except:
pass #First run
categories = {
'Input' : [],
'Parameters' : [],
'Math' : [],
'Vector' : [],
'Color' : [],
'Texturing' : [],
'Shading' : [],
'Filter' : [],
'Other' : [],
'Node Tree' : [],
'Internal' : [],
}
for name in graph.graph_io:
label = name.replace('_',' ')
categories['Node Tree'].append(NodeItem('MaltIONode', label=f'{label} Input', settings=OrderedDict({
'name' : repr(f'{label} Input'),
'is_output' : repr(False),
'io_type' : repr(name),
})))
categories['Node Tree'].append(NodeItem('MaltIONode', label=f'{label} Output', settings=OrderedDict({
'name' : repr(f'{label} Output'),
'is_output' : repr(True),
'io_type' : repr(name),
})))
if graph.language == 'GLSL':
categories['Other'].append(NodeItem('MaltInlineNode', label='Inline Code', settings={
'name' : repr('Inline Code')
}))
categories['Other'].append(NodeItem('MaltArrayIndexNode', label='Array Element', settings={
'name' : repr('Array Element')
}))
subcategories = set()
def add_to_category(dic, node_type):
for k,v in dic.items():
if (is_internal := v['meta'].get('internal')):
category = 'Internal'
else:
category = v['meta'].get('category')
if category is None:
category = v['file'].replace('\\', '/').replace('/', ' - ').replace('.glsl', '').replace('_',' ')
if category not in categories:
categories[category] = []
_node_type = node_type
label = v['meta'].get('label', v['name'])
subcategory = v['meta'].get('subcategory')
settings = OrderedDict({
'name': repr(label),
'malt_label': repr(label)
})
if subcategory and not is_internal:
_node_type = 'MaltFunctionSubCategoryNode'
label = subcategory
settings.update({
'name' : repr(label),
'malt_label': repr(label),
'subcategory': repr(subcategory),
'function_enum': repr(k),
})
func_label = v['meta']['label']
def draw_subcategory_item(self: MaltNodeItem, layout, _context):
props = layout.operator('node.add_subcategory_node', text=self.button_label)
props.settings = repr(self.settings)
#add subcategory functions to the search menu but not to the regular menus
categories['Node Tree'].append(MaltSearchMenuItem(_node_type, category, label=f'{subcategory}: {func_label}', settings=settings))
if subcategory in subcategories:
continue
subcategories.add(subcategory)
node_item = MaltNodeItem(_node_type, category, label=label, settings=settings, draw=draw_subcategory_item)
else:
if node_type == 'MaltFunctionNode':
settings['function_type'] = repr(k)
elif node_type == 'MaltStructNode':
settings['struct_type'] = repr(k)
node_item = MaltNodeItem(_node_type, category, label=label, settings=settings)
categories[category].append(node_item)
add_to_category(functions, 'MaltFunctionNode')
add_to_category(structs, 'MaltStructNode')
def poll(cls, context):
tree = context.space_data.edit_tree
return tree and tree.bl_idname == 'MaltTree' and tree.graph_type == graph.name
def poll_internal(cls, context):
preferences = bpy.context.preferences.addons['BlenderMalt'].preferences
return poll(cls, context) and preferences.show_internal_nodes
category_type = type(category_id, (NodeCategory,),
{
'poll': classmethod(poll),
})
category_internal_type = type(f'{category_id}_INTERNAL', (category_type,),
{
'poll': classmethod(poll_internal),
})
from BlenderMalt import _PLUGINS
for plugin in _PLUGINS:
try:
for category, nodeitems in plugin.blendermalt_register_nodeitems(MaltNodeItem).items():
if category not in categories.keys():
categories[category] = []
categories[category].extend(nodeitems)
except:
import traceback
traceback.print_exc()
category_list = []
for category_name, node_items in categories.items():
if not len(node_items):
continue
bl_id = f'{category_id}_{category_name}'
bl_id = ''.join(c for c in bl_id if c.isalnum())
if len(bl_id) > 64:
bl_id = bl_id[:64]
if category_name == 'Internal':
category_list.append(category_internal_type(bl_id, category_name, items=node_items))
else:
category_list.append(category_type(bl_id, category_name, items=node_items))
register_node_categories(category_id, category_list)
def node_header_ui(self, context):
node_tree = context.space_data.edit_tree
if context.space_data.tree_type != 'MaltTree' or node_tree is None:
return
def duplicate():
context.space_data.node_tree = node_tree.get_copy()
self.layout.operator('wm.malt_callback', text='', icon='DUPLICATE').callback.set(duplicate, 'Duplicate')
def recompile():
node_tree.update_ext(force_update=True)
self.layout.operator("wm.malt_callback", text='', icon='FILE_REFRESH').callback.set(recompile, 'Recompile')
self.layout.prop_search(node_tree, 'graph_type', context.scene.world.malt, 'graph_types',text='')
def get_node_spaces(context):
spaces = []
locked_spaces = []
for window in context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'NODE_EDITOR':
for space in area.spaces:
if space.type == 'NODE_EDITOR' and space.tree_type == 'MaltTree':
if space.pin == False or space.node_tree is None:
spaces.append(space)
else:
locked_spaces.append(space)
return spaces, locked_spaces
def set_node_tree(context, node_tree, node = None):
if context.space_data.type == 'NODE_EDITOR' and context.area.ui_type == 'MaltTree':
context.space_data.path.append(node_tree, node = node)
else:
spaces, locked_spaces = get_node_spaces(context)
if len(spaces) > 0:
spaces[0].node_tree = node_tree
elif len(locked_spaces) > 0:
locked_spaces[0].node_tree = node_tree
def active_material_update(dummy=None):
try:
material = bpy.context.object.active_material
node_tree = material.malt.shader_nodes
except:
node_tree = None
if node_tree:
spaces, locked_spaces = get_node_spaces(bpy.context)
for space in spaces:
if space.node_tree is None or space.node_tree.graph_type == 'Mesh':
space.node_tree = node_tree
return
@bpy.app.handlers.persistent
def depsgraph_update(scene, depsgraph):
# Show the active material node tree in the Node Editor
from BlenderMalt import MaltPipeline
if MaltPipeline.is_malt_active() == False:
return
scene_updated = False
for deps_update in depsgraph.updates:
if isinstance(deps_update.id, bpy.types.Scene):
scene_updated = True
if scene_updated == False:
return
active_material_update()
@bpy.app.handlers.persistent
def load_post(dummy=None):
#msgbus subscriptions can't be persistent across file loads :(
bpy.msgbus.subscribe_rna(
key=(bpy.types.Object, "active_material_index"),
owner=__msgbus_owner,
args=(None,),
notify=active_material_update
)
classes = [
MaltTree,
NODE_PT_MaltNodeTree,
]
__msgbus_owner = object()
def register():
for _class in classes: bpy.utils.register_class(_class)
bpy.types.NODE_HT_header.append(node_header_ui)
bpy.app.timers.register(track_library_changes, persistent=True)
bpy.app.handlers.depsgraph_update_post.append(depsgraph_update)
bpy.app.handlers.load_post.append(load_post)
def unregister():
bpy.msgbus.clear_by_owner(__msgbus_owner)
bpy.app.handlers.load_post.remove(load_post)
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update)
bpy.app.timers.unregister(track_library_changes)
bpy.types.NODE_HT_header.remove(node_header_ui)
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/MaltNodeUITools.py
================================================
import bpy
import string
from typing import Union
from bpy.types import NodeTree
from bpy.props import PointerProperty
from . MaltNodeTree import MaltTree
import blf, gpu
from gpu_extras.batch import batch_for_shader
from mathutils import Vector
class NodeTreePreview(bpy.types.PropertyGroup):
# Name of the node used as a preview
node_name: bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
# Socket name and index are stored to provide fallbacks
socket_name: bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
socket_index: bpy.props.IntProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
@property
def node_tree(self) -> NodeTree:
return self.id_data
def is_socket_valid(self, socket: bpy.types.NodeSocket) -> bool:
return (
socket.node in self.node_tree.nodes.values()
and not socket.is_output
)
def get_node(self) -> Union[bpy.types.Node, None]:
return self.node_tree.nodes.get(self.node_name, None)
def get_socket_ex(self) -> tuple[Union[bpy.types.NodeSocket, None], Union[str, int]]:
'''Gets the stored socket. Because all references have to be strings and ints, the validity of the references have to be checked first.'''
if (node := self.get_node()) == None:
return None, ''
if self.socket_name in node.inputs.keys():
return node.inputs[self.socket_name], self.socket_name
elif len(node.inputs) > self.socket_index:
return node.inputs[self.socket_index], self.socket_index
else:
return None, ''
def get_socket(self) -> Union[bpy.types.NodeSocket, None]:
return self.get_socket_ex()[0]
@staticmethod
def _get_visible_node_sockets(node: bpy.types.Node, get_outputs: bool = False) -> list[bpy.types.NodeSocket]:
front = node.outputs if get_outputs else node.inputs
return [s for s in front.values() if s.enabled and (not s.hide or len(s.links))]
def set_socket(self, socket: bpy.types.NodeSocket) -> bool:
'''Store the given socket as the preview.'''
if not self.is_socket_valid(socket):
return False
self.node_name = socket.node.name
self.socket_name = socket.name
self.socket_index = next(i for i, s in enumerate(self._get_visible_node_sockets(socket.node, get_outputs=False)) if s == socket)
return True
def reset_socket_from_node(self, node: bpy.types.Node) -> bool:
'''Store a socket as the preview of the given node. If a socket of that node is already the preview, cycle through the sockets, otherwise use the first socket.'''
node_inputs = self._get_visible_node_sockets(node, get_outputs=False)
if (input_count := len(node_inputs)) < 1:
return False
old_socket = self.get_socket()
if not self.get_node() == node or old_socket == None:
return self.set_socket(node_inputs[0])
else:
old_index = next(i for i, s in enumerate(node_inputs) if s == old_socket)
return self.set_socket(node_inputs[(old_index + 1) % input_count])
def connect_socket(self, socket: bpy.types.NodeSocket) -> bool:
preview_socket = self.get_socket()
if not preview_socket or preview_socket.node == socket.node or not socket.is_output:
return False
self.node_tree.links.new(socket, preview_socket)
def reconnect_node(self, node: bpy.types.Node) -> bool:
'''Connect a node to the preview socket by cycling through its sockets.'''
preview_socket = self.get_socket()
node_outputs = self._get_visible_node_sockets(node, get_outputs=True)
if not preview_socket or preview_socket.node == node or len(node_outputs) < 1:
return False
try: # Node is already connected to preview socket. Connect next socket
previous_socket = next(
link.from_socket
for link in preview_socket.links
if link.from_socket.node == node and link.from_socket in node_outputs)
previous_index = next(i for i, s in enumerate(node_outputs) if s == previous_socket)
new_socket = node_outputs[(previous_index + 1) % len(node_outputs)]
self.connect_socket(new_socket)
except StopIteration: # When the node is not already connected, use the first socket
self.connect_socket(node_outputs[0])
return True
def is_malt_tree_context(context: bpy.types.Context) -> bool:
return (context.area.ui_type == 'MaltTree' and context.space_data.type == 'NODE_EDITOR' and
context.space_data.edit_tree is not None)
def is_malt_node_context(context: bpy.types.Context) -> bool:
return is_malt_tree_context(context) and context.active_node is not None
class OT_MaltEditNodeTree(bpy.types.Operator):
bl_idname = 'wm.malt_edit_node_tree'
bl_label = 'Edit Node Tree'
bl_description = 'Edit the graph of the active group node'
@classmethod
def poll( cls, context ):
return is_malt_tree_context(context)
def execute( self, context ):
node = context.active_node
space_path = context.space_data.path
node_tree = None
if node and hasattr(node, 'get_pass_node_tree'):
node_tree = node.get_pass_node_tree()
if node_tree:
space_path.append(node_tree, node = node)
else:
space_path.pop()
return {'FINISHED'}
class OT_MaltSetTreePreview(bpy.types.Operator):
bl_idname = 'wm.malt_set_tree_preview'
bl_label = 'Set Tree Preview'
bl_description = 'Set an input socket of the active node as the tree preview socket'
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return is_malt_node_context(context)
def execute(self, context):
node_tree: NodeTree = context.space_data.edit_tree
node = context.active_node
if not node:
return {'CANCELLED'}
node_tree.tree_preview.reset_socket_from_node(node)
context.area.tag_redraw()
return {'FINISHED'}
class OT_MaltConnectTreePreview(bpy.types.Operator):
bl_idname = 'wm.malt_connect_tree_preview'
bl_label = 'Connect To Tree Preview'
bl_description = 'Connect an output of the active node to the node tree preview socket'
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return is_malt_node_context(context)
def execute(self, context):
node_tree: NodeTree = context.space_data.edit_tree
node = context.active_node
if not node:
return {'CANCELLED'}
tp: NodeTreePreview = node_tree.tree_preview
tp.reconnect_node(node)
context.area.tag_redraw()
return {'FINISHED'}
class OT_MaltCycleSubCategories(bpy.types.Operator):
bl_idname = 'wm.malt_cycle_sub_categories'
bl_label = 'Cycle Subcategories'
bl_description = 'Cycle the subcategories of the active subcategory node'
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return is_malt_node_context(context) and context.active_node.bl_idname == 'MaltFunctionSubCategoryNode'
def invoke(self, context: bpy.types.Context, event: bpy.types.Event):
self.node_tree: MaltTree = context.space_data.edit_tree
self.node_tree.disable_updates = True
self.node = context.active_node
self.function_enums:list[tuple[str,str,str]] = self.node.get_function_enums(context)
self.init_function_enum = self.node.function_enum
self.register_interface(True)
wm = context.window_manager
wm.modal_handler_add(self)
return{'RUNNING_MODAL'}
def register_interface(self, register: bool) -> None:
space = bpy.types.SpaceNodeEditor
if register:
self.reset_ui_lists()
self.draw_handler = space.draw_handler_add(self.draw_modal_interface, (self,), 'WINDOW', 'POST_PIXEL')
elif self.draw_handler:
space.draw_handler_remove(self.draw_handler, 'WINDOW')
del self.draw_handler
def reset_ui_lists(self):
'''These lists are being read by the UI callback.'''
self.prev_enums: list[tuple[str, str, str]] = []
self.next_enums: list[tuple[str, str, str]] = []
def cycle_function_enums(self, letter: str, cycle_forward: bool) -> None:
letter = letter.lower()
enum_subset = [enum for enum in self.function_enums if enum[1].lower().startswith(letter)]
if not len(enum_subset):
return #do nothing if there are no possible function_enums with the given letter
self.reset_ui_lists()
new_index = 0 if cycle_forward else len(enum_subset) - 1
new_function_enum = enum_subset[new_index][0]
if self.node.function_enum in (enum[0] for enum in enum_subset):
old_index = next(i for i, enum in enumerate(enum_subset) if enum[0] == self.node.function_enum)
offset = 1 if cycle_forward else -1
new_index = (old_index + offset) % len(enum_subset)
new_function_enum = enum_subset[new_index][0]
self.prev_enums = enum_subset[:new_index]
self.next_enums = enum_subset[new_index + 1:]
self.node.function_enum = new_function_enum
self.node.setup_width()
def modal(self, context: bpy.types.Context, event: bpy.types.Event):
context.area.tag_redraw()
if event.type in ['LEFTMOUSE', 'RET', 'SPACE']:
return self.execute(context)
if event.type in ['ESC', 'RIGHTMOUSE'] and event.value == 'PRESS':
return self.cancel(context)
if event.type in string.ascii_uppercase and event.value == 'PRESS':
self.cycle_function_enums(event.type, not event.shift)
return{'RUNNING_MODAL'}
return{'PASS_THROUGH'}
def execute(self, context: bpy.types.Context):
self.node_tree.disable_updates = False
if self.node.function_enum != self.init_function_enum:
self.node_tree.update_ext(force_update=True)
self.register_interface(False)
context.area.tag_redraw()
return{'FINISHED'}
def cancel(self, context):
self.node.function_enum = self.init_function_enum
self.node_tree.disable_updates = False
self.register_interface(False)
context.area.tag_redraw()
return{'CANCELLED'}
@staticmethod
def draw_modal_interface(operator: 'OT_MaltCycleSubCategories') -> None:
context = bpy.context
node: bpy.types.Node = operator.node
font_id = 0
if context.space_data.path[-1].node_tree != node.id_data:
return
prefs = context.preferences
label_style = prefs.ui_styles[0].widget
zoom = MaltNodeDrawCallbacks.get_view_zoom(context)
dpifac = MaltNodeDrawCallbacks.get_dpifac(context)
to_region_loc = MaltNodeDrawCallbacks.real_region_loc
prev_enums = operator.prev_enums
next_enums = operator.next_enums
text_spacing = 15.0 * zoom
has_display_items = any(len(x) for x in (prev_enums, next_enums))
rect_size = 70 if has_display_items else 40
rect_size *= zoom
top_left = to_region_loc(Vector(node.location), context)
bottom_right = to_region_loc(Vector(node.location) + Vector(node.dimensions) * Vector((1/dpifac, - 1/dpifac)), context)
m_color = Vector((0.0, 0.0, 0.0, 0.8))
t_color = Vector((0.0, 0.0, 0.0, 0.0))
vertices = (
top_left + Vector((0, rect_size)), (bottom_right.x, top_left.y + rect_size),
top_left, (bottom_right.x, top_left.y),
(top_left.x, bottom_right.y), bottom_right,
(top_left.x, bottom_right.y - rect_size), bottom_right + Vector((0, - rect_size))
)
vertex_colors = (
t_color, t_color,
m_color, m_color,
m_color, m_color,
t_color, t_color
)
indices = (
(0,1,2), (2,1,3),
(4,5,6), (6,5,7),
)
shader = gpu.shader.from_builtin('SMOOTH_COLOR')
batch = batch_for_shader(shader, 'TRIS', {'pos': vertices, 'color': vertex_colors}, indices=indices)
gpu.state.blend_set('ALPHA')
batch.draw(shader)
gpu.state.blend_set('NONE')
blf.size(font_id, label_style.points * zoom)
blf.color(font_id, 1,1,1,1)
for i, e in enumerate(reversed(prev_enums)):
loc = top_left + Vector((text_spacing * 0.5, i * text_spacing + text_spacing * 0.5))
blf.position(font_id, *loc, 0)
blf.draw(font_id, e[1])
for i, e in enumerate(next_enums):
loc = Vector((top_left.x, bottom_right.y)) + Vector((text_spacing * 0.5, -((i + 1) * text_spacing)))
blf.position(font_id, *loc, 0)
blf.draw(font_id, e[1])
class NODE_OT_add_malt_subcategory_node(bpy.types.Operator):
bl_idname = 'node.add_subcategory_node'
bl_label = 'Add Malt Subcategory Node'
bl_description = 'Add a new Malt Subcategory node. Automatically invoke subcategory cycling'
bl_options = {'UNDO'}
nodetype: bpy.props.StringProperty(default='MaltFunctionSubCategoryNode')
settings: bpy.props.StringProperty(default='dict()')
@classmethod
def poll(cls, context: bpy.types.Context):
return is_malt_tree_context(context)
def execute(self, context: bpy.types.Context):
for n in context.space_data.edit_tree.nodes:
n.select = False
bpy.ops.node.add_node('INVOKE_DEFAULT', type=self.nodetype, use_transform=True)
node: bpy.types.Node = context.active_node
from collections import OrderedDict #maybe there is a better solution for this but without an exception will be thrown because the class is not imported
for k, v in eval(self.settings).items():
try:
setattr(node, k, eval(v))
except:
print(f'Attribute {repr(k)} could not be set on {repr(node)}')
if context.preferences.addons['BlenderMalt'].preferences.use_subfunction_cycling:
bpy.ops.wm.malt_cycle_sub_categories('INVOKE_DEFAULT')
return{'FINISHED'}
classes = [
NodeTreePreview,
OT_MaltEditNodeTree,
OT_MaltSetTreePreview,
OT_MaltConnectTreePreview,
OT_MaltCycleSubCategories,
NODE_OT_add_malt_subcategory_node,
]
class MaltNodeDrawCallbacks:
@staticmethod
def get_dpifac(context: bpy.types.Context):
p = context.preferences
return p.system.dpi * p.system.pixel_size / 72
@staticmethod
def real_region_loc(view_location: Vector|tuple|list, context: bpy.types.Context) -> Vector:
return Vector(
context.region.view2d.view_to_region(
*[x * MaltNodeDrawCallbacks.get_dpifac(context) for x in view_location],
clip=False
)
)
@staticmethod
def get_view_zoom(context: bpy.types.Context) -> float:
get_loc = MaltNodeDrawCallbacks.real_region_loc
return (get_loc((100, 0), context).x - get_loc((0, 0), context).x) / 100.0
@staticmethod
def context_path_ui_callback():
import blf
font_id = 0
context = bpy.context
space = context.space_data
if not is_malt_tree_context(context):
return
if not space.overlay.show_context_path:
return
path = space.path
text = ' > '.join(x.node_tree.name for x in path)
preferences = context.preferences
ui_scale = preferences.view.ui_scale
size = preferences.ui_styles[0].widget.points * ui_scale
color = preferences.themes[0].node_editor.space.text
dpi = preferences.system.dpi
blf.size(font_id, size * (dpi / 72.0))
blf.position(font_id, 10, 10, 0)
blf.color(font_id, *color, 1)
blf.draw(font_id, text)
@staticmethod
def tree_preview_ui_callback():
font_id = 0
context = bpy.context
space:bpy.types.SpaceNodeEditor = context.space_data
if not is_malt_tree_context(context):
return
node_tree: MaltTree = space.edit_tree
tp:NodeTreePreview = node_tree.tree_preview
socket, identifier = tp.get_socket_ex()
if socket == None:
return
preferences = context.preferences
label_style = preferences.ui_styles[0].widget
size = label_style.points
#calculate the zoom of the view by taking the difference of transformed points in the x-axis
zoom = MaltNodeDrawCallbacks.get_view_zoom(context)
node = socket.node
view_loc = Vector(node.location) + Vector((5,5))
region_loc = MaltNodeDrawCallbacks.real_region_loc(view_loc, context)
text = f'Preview: {repr(identifier)}'
#mix the socket color with white to get a result thats not too dark
color = (Vector(socket.draw_color(context, node)) + Vector((1,1,1,1))) * 0.5
def draw_text(text: str, size: float, loc: tuple[float, float], color: tuple[float, float, float, float]):
blf.size(font_id, size)
blf.position(font_id, *loc, 0)
blf.color(font_id, *color)
blf.draw(font_id, text)
draw_text(text, size * zoom, tuple(region_loc + Vector((0,-1))), (0,0,0,1))
draw_text(text, size * zoom, region_loc, color)
keymaps = []
def register_node_tree_shortcuts():
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
def add_shortcut(keymaps: list, operator: bpy.types.Operator, *, type: str, value: str, **modifiers):
km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
kmi = km.keymap_items.new(operator.bl_idname, type=type, value=value, **modifiers)
keymaps.append((km, kmi))
if kc:
add_shortcut(keymaps, OT_MaltEditNodeTree, type='TAB', value='PRESS')
add_shortcut(keymaps, OT_MaltSetTreePreview, type='LEFTMOUSE', value='PRESS', shift=True, alt=True)
add_shortcut(keymaps, OT_MaltConnectTreePreview, type='LEFTMOUSE', value='PRESS', shift=True, ctrl=True)
def register():
for _class in classes: bpy.utils.register_class(_class)
global CONTEXT_PATH_DRAW_HANDLER, TREE_PREVIEW_DRAW_HANDLER
CONTEXT_PATH_DRAW_HANDLER = bpy.types.SpaceNodeEditor.draw_handler_add(MaltNodeDrawCallbacks.context_path_ui_callback, (), 'WINDOW', 'POST_PIXEL')
TREE_PREVIEW_DRAW_HANDLER = bpy.types.SpaceNodeEditor.draw_handler_add(MaltNodeDrawCallbacks.tree_preview_ui_callback, (), 'WINDOW', 'POST_PIXEL')
register_node_tree_shortcuts()
NodeTree.tree_preview = PointerProperty(type=NodeTreePreview, name='Node Tree Preview',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def unregister():
del NodeTree.tree_preview
for km, kmi in keymaps:
km.keymap_items.remove(kmi)
keymaps.clear()
global CONTEXT_PATH_DRAW_HANDLER, TREE_PREVIEW_DRAW_HANDLER
bpy.types.SpaceNodeEditor.draw_handler_remove(CONTEXT_PATH_DRAW_HANDLER, 'WINDOW')
bpy.types.SpaceNodeEditor.draw_handler_remove(TREE_PREVIEW_DRAW_HANDLER, 'WINDOW')
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/MaltSocket.py
================================================
import bpy
__TYPE_COLORS = {
'bool': (0.8, 0.65, 0.84, 1.0),
'Bool': (0.8, 0.65, 0.84, 1.0),
'float': (0.63, 0.63, 0.63, 1.0),
'Float': (0.63, 0.63, 0.63, 1.0),
'int': (0.33, 0.55, 0.36, 1.0),
'Int': (0.33, 0.55, 0.36, 1.0),
'vec2': (0.39, 0.74, 0.78, 1.0),
'vec3': (0.39, 0.39, 0.78, 1.0),
'vec4': (0.60, 0.39, 0.78, 1.0),
'mat4': (0.78, 0.39, 0.58, 1.0),
'sampler1D': (0.78, 0.78, 0.46, 1.0),
'sampler2D': (0.78, 0.64, 0.16, 1.0),
'Texture': (0.78, 0.64, 0.16, 1.0),
'Scene': (1.0, 1.0, 1.0, 1.0),
}
def get_type_color(type):
if type not in __TYPE_COLORS:
import random, hashlib
seed = hashlib.sha1(type.encode('ascii')).digest()
rand = random.Random(seed)
__TYPE_COLORS[type] = (rand.random(),rand.random(),rand.random(),1.0)
return __TYPE_COLORS[type]
class MaltSocket(bpy.types.NodeSocket):
bl_label = "Malt Node Socket"
def on_type_update(self, context):
self.node.on_socket_update(self)
data_type: bpy.props.StringProperty(update=on_type_update,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
array_size: bpy.props.IntProperty(default=0, update=on_type_update,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
default_initialization: bpy.props.StringProperty(default='',
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def show_in_material_panel_update(self, context=None):
key = self.node.get_input_parameter_name(self.name)
show_in_children = self.node.id_data.malt_parameters.show_in_children
if key in show_in_children.keys():
show_in_children[key].boolean = self.show_in_material_panel
show_in_material_panel: bpy.props.BoolProperty(default=True, update=show_in_material_panel_update,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
active: bpy.props.BoolProperty(default=True,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
ui_label: bpy.props.StringProperty()
def is_instantiable_type(self):
return self.data_type.startswith('sampler') == False
def get_source_reference(self, target_type=None):
assert(self.active)
if not self.is_instantiable_type() and not self.is_output and self.get_linked() is not None:
self.get_linked().get_source_reference()
else:
reference = self.node.get_source_socket_reference(self)
if target_type and target_type != self.data_type:
cast_function = self.node.id_data.cast(self.data_type, target_type)
return f'{cast_function}({reference})'
return reference
def get_source_global_reference(self):
assert(self.active)
transpiler = self.id_data.get_transpiler()
result = transpiler.global_reference(self.node.get_source_name(), self.name)
if len(result) > 63:
#Blender dictionary keys are limited to 63 characters
import xxhash
result = result[:59] + xxhash.xxh32_hexdigest(result)[:4]
return result
def is_struct_member(self):
return '.' in self.name
def get_struct_socket(self):
if self.is_struct_member():
struct_socket_name = self.name.split('.')[0]
if self.is_output:
return self.node.outputs[struct_socket_name]
else:
return self.node.inputs[struct_socket_name]
return None
def get_source_initialization(self):
assert(self.active)
if self.get_linked():
return self.get_linked().get_source_reference(self.data_type)
elif self.default_initialization != '':
return self.default_initialization
elif self.is_struct_member() and (self.get_struct_socket().get_linked() or self.get_struct_socket().default_initialization != ''):
return None
else:
return self.get_source_global_reference()
def get_linked(self, ignore_muted=True):
def get_linked_internal(socket):
if socket.is_linked == False and ignore_muted:
return None
else:
try: link = socket.links[0]
except: return None #socket.links can be empty even if is_linked is true!?!?!
if ignore_muted and link.is_muted:
return None
linked = link.to_socket if socket.is_output else link.from_socket
if isinstance(linked.node, bpy.types.NodeReroute):
sockets = linked.node.inputs if linked.is_output else linked.node.outputs
if len(sockets) == 0:
return None
return get_linked_internal(sockets[0])
else:
return linked if linked.active else None
return get_linked_internal(self)
def get_ui_label(self, print_type=True):
name = self.ui_label
if print_type == False:
return name
type = self.data_type
if self.array_size > 0:
type += f'[{self.array_size}]'
if self.is_output:
return f'({type}) : {name}'
else:
return f'{name} : ({type})'
def draw(self, context, layout, node, text):
if self.active == False:
layout.active = False
layout.label(text=text)
elif context.region.type != 'UI' or self.get_source_global_reference() == self.get_source_initialization():
preferences = bpy.context.preferences.addons['BlenderMalt'].preferences
text = self.get_ui_label(preferences.show_socket_types)
node.draw_socket(context, layout, self, text)
if context.region.type == 'UI':
icon = 'HIDE_OFF' if self.show_in_material_panel else 'HIDE_ON'
layout.prop(self, 'show_in_material_panel', text='', icon=icon)
def setup_shape(self):
from Malt.PipelineParameters import Parameter
base_type = True
try:
Parameter.from_glsl_type(self.data_type)
except:
base_type = False
array_type = self.array_size > 0
if base_type:
if array_type:
self.display_shape = 'CIRCLE_DOT'
else:
self.display_shape = 'CIRCLE'
else:
if array_type:
self.display_shape = 'SQUARE_DOT'
else:
self.display_shape = 'SQUARE'
def draw_color(self, context, node):
color = get_type_color(self.data_type)
if self.active == False:
color = list(color)
color[3] = 0.25
return color
classes = [
MaltSocket
]
def register():
for _class in classes: bpy.utils.register_class(_class)
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/Nodes/MaltArrayIndexNode.py
================================================
from Malt.PipelineParameters import Parameter, Type
import bpy
from BlenderMalt.MaltNodes.MaltNode import MaltNode
class MaltArrayIndexNode(bpy.types.Node, MaltNode):
bl_label = "Array Index Node"
def malt_init(self):
self.setup()
def malt_setup(self, copy=None):
self.setup_sockets({ 'array' : {'type': '', 'size': 1}, 'index' : {'type': Parameter(0, Type.INT) }},
{'element' : {'type': ''} }, copy=copy)
def malt_update(self):
inputs = {
'array' : {'type': '', 'size': 1},
'index' : {'type': 'int', 'meta':{'value':'0'} }
}
outputs = { 'element' : {'type': ''} }
linked = self.inputs['array'].get_linked()
if linked and linked.array_size > 0:
inputs['array']['type'] = linked.data_type
inputs['array']['size'] = linked.array_size
outputs['element']['type'] = linked.data_type
self.setup_sockets(inputs, outputs)
def get_source_socket_reference(self, socket):
return '{}_0_{}'.format(self.get_source_name(), socket.name)
def get_source_code(self, transpiler):
array = self.inputs['array']
index = self.inputs['index']
element = self.outputs['element']
element_reference = index.get_source_global_reference()
if index.get_linked():
element_reference = index.get_linked().get_source_reference()
initialization = '{}[{}]'.format(array.get_linked().get_source_reference(), element_reference)
return transpiler.declaration(element.data_type, element.array_size, element.get_source_reference(), initialization)
classes = [
MaltArrayIndexNode,
]
def register():
for _class in classes: bpy.utils.register_class(_class)
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/Nodes/MaltFunctionNode.py
================================================
from Malt.PipelineParameters import Type, Parameter, MaterialParameter, GraphParameter
import bpy
from BlenderMalt.MaltNodes.MaltNode import MaltNode
class MaltFunctionNodeBase(MaltNode):
def malt_setup(self, copy=None):
pass_type = self.get_pass_type()
if pass_type != '':
self.pass_graph_type, self.pass_graph_io_type = pass_type.split('.')
function = self.get_function(skip_overrides=False, find_replacement=True)
inputs = {}
outputs = {}
if function['type'] != 'void':
outputs['result'] = {'type': function['type']} #TODO: Array return type
for parameter in function['parameters']:
if parameter['io'] in ['out','inout']:
outputs[parameter['name']] = parameter
if parameter['io'] in ['','in','inout']:
inputs[parameter['name']] = parameter
show_in_material_panel = function['meta'].get('category', '') == 'Parameters'
self.setup_sockets(inputs, outputs, show_in_material_panel=show_in_material_panel, copy=copy)
if self.pass_graph_type != '':
graph = self.id_data.get_pipeline_graph(self.pass_graph_type)
if graph.graph_type == graph.GLOBAL_GRAPH:
if graph.language == 'Python':
self.malt_parameters.setup(
{'PASS_GRAPH': GraphParameter(None, graph.name)},
replace_parameters=False,
skip_private=False
)
else:
self.malt_parameters.setup(
{'PASS_MATERIAL': MaterialParameter(None, graph.file_extension, self.pass_graph_type)},
replace_parameters=False,
skip_private=False
)
function_type : bpy.props.StringProperty(update=MaltNode.setup,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
pass_graph_type: bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
pass_graph_io_type: bpy.props.StringProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_parameters(self, overrides, resources):
parameters = super().get_parameters(overrides, resources)
parameters['CUSTOM_IO'] = self.get_custom_io()
if 'PASS_GRAPH' in self.malt_parameters.graphs.keys():
try: parameters['PASS_GRAPH'] = self.malt_parameters.get_parameter('PASS_GRAPH', overrides, resources)
except: pass
if 'PASS_MATERIAL' in self.malt_parameters.materials.keys():
try: parameters['PASS_MATERIAL'] = self.malt_parameters.get_parameter('PASS_MATERIAL', overrides, resources)
except: pass
return parameters
def find_replacement_function(self):
print(f'Try to find replacement function for "{self.name}"')
print(f'Current function is "{self.function_type}"')
library = self.id_data.get_full_library()['functions']
for key, function in library.items():
try:
for replace in eval(function['meta']['replace']).split(','):
if replace in self.function_type:
self.function_type = key
return function
except:
pass
parameters = set()
from itertools import chain
for socket in chain(self.inputs.keys(), self.outputs.keys()):
parameters.add(socket)
key, function = None, None
matching_parameters = 0
total_parameters = 0
for _key, _function in library.items():
if _function['name'] in self.function_type:
matching_count = 0
for parameter in _function['parameters']:
if parameter['name'] in parameters:
matching_count += 1
if (key is None or matching_count > matching_parameters or
(matching_count == matching_parameters and len(_function['parameters']) < total_parameters)):
total_parameters = len(_function['parameters'])
key = _key
function = _function
if key:
print(f'Found replacement function: "{key}"')
self.function_type = key
return function
else:
self.select = True
def get_function(self, skip_overrides=True, find_replacement=False):
graph = self.id_data.get_pipeline_graph()
function = None
if self.function_type in graph.functions:
function = graph.functions[self.function_type]
elif self.function_type in self.id_data.get_library()['functions']:
function = self.id_data.get_library()['functions'][self.function_type]
elif find_replacement:
function = self.find_replacement_function()
if function:
from copy import deepcopy
function = deepcopy(function)
function['parameters'] += self.get_custom_io()
if skip_overrides:
function['parameters'] = [p for p in function['parameters'] if '@' not in p['name']]
return function
def get_pass_type(self):
graph = self.id_data.get_pipeline_graph()
if graph.language == 'Python':
pass_type = graph.functions[self.function_type]['pass_type']
if pass_type:
return pass_type
return ''
def get_pass_node_tree(self):
if self.pass_graph_type != '':
graph = self.id_data.get_pipeline_graph(self.pass_graph_type)
if graph.graph_type == graph.GLOBAL_GRAPH:
if graph.language == 'Python':
return self.malt_parameters.graphs['PASS_GRAPH'].graph
else:
material = self.malt_parameters.materials['PASS_MATERIAL'].material
if material:
return material.malt.shader_nodes
def get_custom_io(self):
if self.pass_graph_type != '':
graph = self.id_data.get_pipeline_graph(self.pass_graph_type)
if graph.graph_type == graph.GLOBAL_GRAPH:
tree = None
if graph.language == 'Python':
if 'PASS_GRAPH' in self.malt_parameters.graphs.keys():
tree = self.malt_parameters.graphs['PASS_GRAPH'].graph
else:
if 'PASS_MATERIAL' in self.malt_parameters.materials.keys():
material = self.malt_parameters.materials['PASS_MATERIAL'].material
if material:
tree = material.malt.shader_nodes
if tree:
return tree.get_custom_io(self.pass_graph_io_type)
else:
world = bpy.context.scene.world
if world:
custom_io = world.malt_graph_types[self.pass_graph_type].custom_passes['Default'].io[self.pass_graph_io_type]
result = []
for parameter in custom_io.inputs:
result.append({
'name': parameter.name,
'type': 'Texture', #TODO
'subtype': parameter.parameter,
'size': 0,
'io': 'in',
})
for parameter in custom_io.outputs:
result.append({
'name': parameter.name,
'type': 'Texture', #TODO
'subtype': parameter.parameter,
'size': 0,
'io': 'out',
})
return result
return []
def get_source_socket_reference(self, socket):
transpiler = self.id_data.get_transpiler()
if transpiler.is_instantiable_type(socket.data_type):
return transpiler.parameter_reference(self.get_source_name(), socket.name, 'out' if socket.is_output else 'in')
else:
source = self.get_source_code(transpiler)
return source.splitlines()[-1].split('=')[-1].split(';')[0]
def get_source_code(self, transpiler):
function = self.get_function()
source_name = self.get_source_name()
parameters = []
post_parameter_initialization = ''
for input in self.inputs:
if input.active and input.is_struct_member():
initialization = input.get_source_initialization()
if initialization:
post_parameter_initialization += transpiler.asignment(input.get_source_reference(), initialization)
for parameter in function['parameters']:
initialization = None
if parameter['io'] in ['','in','inout']:
socket = self.inputs[parameter['name']]
initialization = socket.get_source_initialization()
parameters.append(initialization)
if self.pass_graph_type != '':
def add_implicit_parameter(name):
parameter = transpiler.parameter_reference(self.get_source_name(), name, None)
initialization = transpiler.global_reference(self.get_source_name(), name)
nonlocal post_parameter_initialization
post_parameter_initialization += transpiler.asignment(parameter, initialization)
graph = self.id_data.get_pipeline_graph(self.pass_graph_type)
if graph.graph_type == graph.GLOBAL_GRAPH:
if graph.language == 'Python':
add_implicit_parameter('PASS_GRAPH')
else:
add_implicit_parameter('PASS_MATERIAL')
add_implicit_parameter('CUSTOM_IO')
return transpiler.call(function, source_name, parameters, post_parameter_initialization)
def draw_buttons(self, context, layout):
if self.pass_graph_type != '':
layout.operator('wm.malt_callback', text='Reload Sockets', icon='FILE_REFRESH').callback.set(self.setup, 'Reload Sockets')
graph = self.id_data.get_pipeline_graph(self.pass_graph_type)
if graph.graph_type == graph.GLOBAL_GRAPH:
if graph.language == 'Python':
self.malt_parameters.draw_parameter(layout, 'PASS_GRAPH', None, is_node_socket=True)
else:
self.malt_parameters.draw_parameter(layout, 'PASS_MATERIAL', None, is_node_socket=True)
class MaltFunctionNode(bpy.types.Node, MaltFunctionNodeBase):
bl_label = "Function Node"
classes = [
MaltFunctionNode,
]
def register():
for _class in classes: bpy.utils.register_class(_class)
def unregister():
for _class in reversed(classes): bpy.utils.unregister_class(_class)
================================================
FILE: BlenderMalt/MaltNodes/Nodes/MaltFunctionSubCategory.py
================================================
import bpy
from BlenderMalt.MaltNodes.Nodes.MaltFunctionNode import MaltFunctionNodeBase
class MaltFunctionSubCategoryNode(bpy.types.Node, MaltFunctionNodeBase):
bl_label = "Function SubCategory Node"
subcategory : bpy.props.StringProperty(options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_function_enums(self, context=None):
library = self.id_data.get_full_library()
items = []
for key in library['subcategories'][self.subcategory]:
function = library['functions'].get(key)
if function is None or function['meta'].get('internal'):
continue
label = function['meta'].get('label')
if label is None:
label = function['name'].replace('_',' ').title()
items.append((key, label, label))
return items
def update_function_enum(self, context=None):
self.function_type = self.function_enum
if self.disable_updates == False:
self.id_data.update_ext(force_update=True)
function_enum : bpy.props.EnumProperty(name='', items=get_function_enums, update=update_function_enum,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def malt_setup(self, copy=None):
#Keep the correct function when the subcategory list changes
if self.function_type != '' and self.function_type != self.function_enum:
try: self.function_enum = self.function_type
except:
try:
self.function_type = self.function_enum
except:
self.function_type = self.get_function_enums()[0][0]
return super().malt_setup(copy=copy)
def should_delete_outdated_links(self):
return True
def draw_buttons(self, context, layout):
r = layout.row(align=True)
r.context_pointer_set('active_node', self)
r.prop(self, 'function_enum')
if context.preferences.addons['BlenderMalt'].preferences.use_subfunction_cycling:
r.operator('wm.malt_cycle_sub_categories', text='', icon='COLLAPSEMENU')
return super().draw_buttons(context, layout)
def calc_node_width(self, point_size) -> float:
import blf
blf.size(0, point_size)
max_width = super().calc_node_width(point_size)
layout_padding = 70 # account for the spaces on both sides of the enum dropdown
label = next(enum[1] for enum in self.get_function_enums() if enum[0]==self.function_enum)
return max(max_width, blf.dimensions(0, label)[0] + layout_padding)
def draw_label(self):
label = super().draw_label()
if self.hide:
label += ' - ' + self.get_function()['meta'].get('label', None)
return label
def register():
bpy.utils.register_class(MaltFunctionSubCategoryNode)
def unregister():
bpy.utils.unregister_class(MaltFunctionSubCategoryNode)
================================================
FILE: BlenderMalt/MaltNodes/Nodes/MaltIONode.py
================================================
import bpy
from BlenderMalt.MaltNodes.MaltNode import MaltNode
from BlenderMalt.MaltProperties import MaltPropertyGroup
from BlenderMalt.MaltNodes.MaltCustomPasses import *
class MaltIONode(bpy.types.Node, MaltNode):
bl_label = "IO Node"
properties: bpy.props.PointerProperty(type=MaltPropertyGroup,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
is_output: bpy.props.BoolProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_custom_pass_enums(self, context):
custom_passes = ['Default']
if self.allow_custom_pass:
custom_passes = context.scene.world.malt_graph_types[self.id_data.graph_type].custom_passes.keys()
return [(p,p,p) for p in custom_passes]
custom_pass: bpy.props.EnumProperty(items=get_custom_pass_enums,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
allow_custom_pass : bpy.props.BoolProperty(default=False,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
allow_custom_parameters : bpy.props.BoolProperty(default=False,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def malt_setup(self, copy=None):
function = self.get_function()
self.graph_type = self.id_data.graph_type
self.pass_type = self.io_type
graph = self.id_data.get_pipeline_graph()
self.allow_custom_pass = graph.graph_type == graph.SCENE_GRAPH
self.allow_custom_parameters = len(self.get_dynamic_parameter_types()) > 0
inputs = {}
outputs = {}
if function['type'] != 'void' and self.is_output:
inputs['result'] = {'type': function['type']}
for parameter in function['parameters']:
if parameter['io'] in ['out','inout'] and self.is_output:
if parameter['io'] == 'inout':
if 'meta' not in parameter: parameter['meta'] = {}
parameter['meta']['default_initialization'] = parameter['name']
inputs[parameter['name']] = parameter
if parameter['io'] in ['','in','inout'] and self.is_output == False:
outputs[parameter['name']] = parameter
for parameter in self.get_custom_parameters():
list = inputs if self.is_output else outputs
if parameter.name not in list.keys(): #Don't override properties
list[parameter.name] = {
'type': parameter.parameter
}
self.setup_sockets(inputs, outputs, copy=copy)
io_type : bpy.props.StringProperty(update=MaltNode.setup,
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
custom_parameters : bpy.props.CollectionProperty(type=MaltIOParameter,
options={'LIBRARY_EDITABLE'},
override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'})
custom_parameters_index : bpy.props.IntProperty(
options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
def get_function(self):
graph = self.id_data.get_pipeline_graph()
return graph.graph_io[self.io_type].function
def get_custom_pass_io(self):
if self.allow_custom_pass and self.allow_custom_parameters:
world = bpy.context.scene.world
return world.malt_graph_types[self.id_data.graph_type].custom_passes[self.custom_pass].io[self.io_type]
def get_custom_parameters(self):
if self.allow_custom_parameters:
if self.allow_custom_pass:
io = self.get_custom_pass_io()
return io.outputs if self.is_output else io.inputs
else:
return self.custom_parameters
else:
return {}
def get_dynamic_parameter_types(self):
graph = self.id_data.get_pipeline_graph()
if self.is_output:
return graph.graph_io[self.io_type].dynamic_output_types
else:
return graph.graph_io[self.io_type].dynamic_input_types
def is_custom_socket(self, socket):
parameters = [parameter['name'] for parameter in self.get_function()['parameters']]
if (socket.name == 'result' and self.is_output) or socket.name in parameters:
return False
elif socket.name in self.get_custom_parameters().keys():
return True
else:
return False
def get_source_socket_reference(self, socket):
transpiler = self.id_data.get_transpiler()
io = 'out' if self.is_output else 'in'
if self.is_custom_socket(socket):
return transpiler.custom_io_reference(io, self.io_type, socket.name)
else:
return transpiler.io_parameter_reference(socket.name, io)
def get_source_code(self, transpiler):
code = ''
if self.is_output:
function = self.get_function()
custom_outputs = ''
for socket in self.inputs:
if socket.active == False:
continue
if self.is_custom_socket(socket):
custom_outputs += transpiler.asignment(self.get_source_socket_reference(socket), socket.get_source_initialization())
else:
if socket.name == 'result':
code += transpiler.declaration(socket.data_type, socket.array_size, socket.name)
initialization = socket.get_source_initialization()
if initialization:
code += transpiler.asignment(socket.get_source_reference(), initialization)
if custom_outputs != '':
graph_io = self.id_data.get_pipeline_graph().graph_io[self.io_type]
try: io_wrap = graph_io.io_wrap
except: io_wrap = ''
code += transpiler.preprocessor_wrap(io_wrap, custom_outputs)
if function['type'] != 'void':
code += transpiler.result(self.inputs['result'].get_source_reference())
return code
def get_source_global_parameters(self, transpiler):
src = MaltNode.get_source_global_parameters(self, transpiler)
custom_outputs = ''
graph_io = self.id_data.get_pipeline_graph().graph_io[self.io_type]
index = graph_io.custom_output_start_index
for key, parameter in self.get_custom_parameters().items():
if parameter.is_output:
socket = self.inputs[key]
custom_outputs += transpiler.custom_output_declaration(socket.data_type, key, index, self.io_type)
index += 1
else:
socket = self.outputs[key]
src += transpiler.global_declaration(parameter.parameter, 0, self.get_source_socket_reference(socket))
if custom_outputs != '':
try: io_wrap = graph_io.io_wrap
except: io_wrap = ''
src += transpiler.preprocessor_wrap(io_wrap, custom_outputs)
return src
def draw_buttons(self, context, layout):
return #TODO: only 1 custom pass signature for now
if self.allow_custom_pass and (self.is_output or self.allow_custom_parameters):
row = layout.row(align=True)
row.prop(self, 'custom_pass', text='Custom Pass')
row.operator('wm.malt_add_custom_pass', text='', icon='ADD').graph_type = self.id_data.graph_type
if self.custom_pass != 'Default':
def remove():
custom_passes = context.scene.world.malt_graph_types[self.id_data.graph_type].custom_passes
custom_passes.remove(custom_passes.find(self.custom_pass))
#self.custom_pass = 'Default'
row.operator('wm.malt_callback', text='', icon='REMOVE').callback.set(remove)
def draw_buttons_ext(self, context, layout):
if self.allow_custom_parameters:
def refresh():
#TODO: Overkill
for tree in bpy.data.node_groups:
if tree.bl_idname == 'MaltTree':
tree.reload_nodes()
tree.update_ext(force_update=True)
layout.operator("wm.malt_callback", text='Reload', icon='FILE_REFRESH').callback.set(refresh, 'Reload')
def draw_parameters_list(owner, parameters_key):
row = layout.row()
index_key = f'{parameters_key}_index'
row.template_list('COMMON_UL_UI_List', '', owner, parameters_key, owner, index_key)
col = row.column()
parameters = getattr(owner, parameters_key)
index = getattr(owner, index_key)
def add_custom_socket():
new_param = pa
gitextract_fmgxqm5w/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── config.yml
│ └── workflows/
│ └── BlenderMalt.yml
├── .gitignore
├── BlenderMalt/
│ ├── CBlenderMalt/
│ │ ├── CBlenderMalt.cpp
│ │ ├── CMakeLists.txt
│ │ ├── __init__.py
│ │ ├── build.py
│ │ ├── mikktspace.c
│ │ └── mikktspace.h
│ ├── MaltLights.py
│ ├── MaltMaterial.py
│ ├── MaltMeshes.py
│ ├── MaltNodes/
│ │ ├── MaltCustomPasses.py
│ │ ├── MaltNode.py
│ │ ├── MaltNodeTree.py
│ │ ├── MaltNodeUITools.py
│ │ ├── MaltSocket.py
│ │ ├── Nodes/
│ │ │ ├── MaltArrayIndexNode.py
│ │ │ ├── MaltFunctionNode.py
│ │ │ ├── MaltFunctionSubCategory.py
│ │ │ ├── MaltIONode.py
│ │ │ ├── MaltInlineNode.py
│ │ │ └── MaltStructNode.py
│ │ └── _init_.py
│ ├── MaltPipeline.py
│ ├── MaltProperties.py
│ ├── MaltRenderEngine.py
│ ├── MaltTextures.py
│ ├── MaltUtils.py
│ ├── __init__.py
│ └── readme.md
├── Bridge/
│ ├── Client_API.py
│ ├── Docs.py
│ ├── Material.py
│ ├── Mesh.py
│ ├── Proxys.py
│ ├── Server.py
│ ├── Texture.py
│ ├── __init__.py
│ ├── ipc/
│ │ ├── CMakeLists.txt
│ │ ├── __init__.py
│ │ ├── build.py
│ │ ├── ipc.c
│ │ └── ipc.h
│ └── renderdoc/
│ ├── CMakeLists.txt
│ ├── __init__.py
│ ├── build.py
│ ├── renderdoc_app.h
│ └── renderdoc_wrapper.c
├── LICENSE
├── LICENSE - DEPENDENCIES
├── LICENSE - DEPENDENCIES (FULL TEXT)
├── Malt/
│ ├── GL/
│ │ ├── GL.py
│ │ ├── GLSLEval.py
│ │ ├── GLSLParser/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── build.py
│ │ │ ├── external/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── PEGTL-LICENSE
│ │ │ │ └── rapidjson-LICENSE
│ │ │ └── src/
│ │ │ └── main.cpp
│ │ ├── Mesh.py
│ │ ├── RenderTarget.py
│ │ ├── Shader.py
│ │ ├── Texture.py
│ │ └── readme.md
│ ├── Nodes/
│ │ ├── LineRender.py
│ │ ├── SceneFilter.py
│ │ └── SuperSamplingAA.py
│ ├── Pipeline.py
│ ├── PipelineGraph.py
│ ├── PipelineNode.py
│ ├── PipelineParameters.py
│ ├── PipelinePlugin.py
│ ├── Pipelines/
│ │ ├── MiniPipeline/
│ │ │ └── MiniPipeline.py
│ │ └── NPR_Pipeline/
│ │ ├── Defaults/
│ │ │ └── defaults.blend
│ │ ├── NPR_LightShaders.py
│ │ ├── NPR_Lighting.py
│ │ ├── NPR_Pipeline.py
│ │ ├── Nodes/
│ │ │ ├── Render/
│ │ │ │ ├── RenderLayers.py
│ │ │ │ ├── SceneLighting.py
│ │ │ │ └── ScreenPass.py
│ │ │ └── RenderLayer/
│ │ │ ├── MainPass.py
│ │ │ ├── PrePass.py
│ │ │ └── ScreenPass.py
│ │ └── Shaders/
│ │ ├── NPR_Intellisense.glsl
│ │ ├── NPR_LightShader.glsl
│ │ ├── NPR_MeshShader.glsl
│ │ ├── NPR_Pipeline/
│ │ │ ├── NPR_Filters.glsl
│ │ │ ├── NPR_Lighting.glsl
│ │ │ ├── NPR_Mesh.glsl
│ │ │ ├── NPR_Shading.glsl
│ │ │ └── NPR_Shading2.glsl
│ │ └── NPR_ScreenShader.glsl
│ ├── Render/
│ │ ├── Common.py
│ │ ├── DepthToCompositeDepth.py
│ │ ├── Lighting.py
│ │ ├── Sampling.py
│ │ └── readme.md
│ ├── Scene.py
│ ├── Shaders/
│ │ ├── Common/
│ │ │ ├── Color.glsl
│ │ │ ├── Hash.glsl
│ │ │ ├── Mapping.glsl
│ │ │ ├── Math.glsl
│ │ │ ├── Matrix.glsl
│ │ │ ├── Normal.glsl
│ │ │ ├── Quaternion.glsl
│ │ │ └── Transform.glsl
│ │ ├── Common.glsl
│ │ ├── Filters/
│ │ │ ├── AO.glsl
│ │ │ ├── Bevel.glsl
│ │ │ ├── Blur.glsl
│ │ │ ├── Curvature.glsl
│ │ │ ├── JumpFlood.glsl
│ │ │ ├── Kuwahara.glsl
│ │ │ ├── Line.glsl
│ │ │ ├── Sharpen.glsl
│ │ │ └── StructureTensor.glsl
│ │ ├── Intellisense/
│ │ │ └── intellisense.glsl
│ │ ├── Lighting/
│ │ │ └── Lighting.glsl
│ │ ├── Node Utils/
│ │ │ ├── bool.glsl
│ │ │ ├── common.glsl
│ │ │ ├── float.glsl
│ │ │ ├── int.glsl
│ │ │ ├── node_utils.glsl
│ │ │ ├── packing.glsl
│ │ │ ├── properties.glsl
│ │ │ ├── sampler.glsl
│ │ │ ├── vec2.glsl
│ │ │ ├── vec3.glsl
│ │ │ └── vec4.glsl
│ │ ├── Node Utils 2/
│ │ │ ├── Bool.glsl
│ │ │ ├── Color.glsl
│ │ │ ├── ColorBlend.glsl
│ │ │ ├── Filter.glsl
│ │ │ ├── Float.glsl
│ │ │ ├── Input.glsl
│ │ │ ├── Int.glsl
│ │ │ ├── Mat4.glsl
│ │ │ ├── Parameters.glsl
│ │ │ ├── Texturing.glsl
│ │ │ ├── Vec2.glsl
│ │ │ ├── Vec3.glsl
│ │ │ ├── Vec4.glsl
│ │ │ ├── Vector.glsl
│ │ │ ├── conversion.glsl
│ │ │ └── node_utils_2.glsl
│ │ ├── Passes/
│ │ │ ├── BlendTexture.glsl
│ │ │ ├── BlendTransparency.glsl
│ │ │ ├── CopyTextures.glsl
│ │ │ ├── DepthToBlenderDepth.glsl
│ │ │ ├── JumpFlood.glsl
│ │ │ ├── LineComposite.glsl
│ │ │ ├── Unpack8bitTextures.glsl
│ │ │ └── sRGBConversion.glsl
│ │ ├── Procedural/
│ │ │ ├── Bayer.glsl
│ │ │ ├── Cell_Noise.glsl
│ │ │ ├── Cell_Noise.inl
│ │ │ ├── Fractal_Noise.glsl
│ │ │ ├── Fractal_Noise.inl
│ │ │ ├── Noise.glsl
│ │ │ └── Noise.inl
│ │ ├── SDF/
│ │ │ └── SDF.glsl
│ │ ├── Shading/
│ │ │ ├── BRDF.glsl
│ │ │ └── ShadingModels.glsl
│ │ └── readme.md
│ ├── SourceTranspiler.py
│ ├── Utils.py
│ ├── __init__.py
│ └── readme.md
├── README.md
├── __init__.py
├── docs/
│ ├── Documentation/
│ │ ├── Getting Started.md
│ │ ├── Graphs.md
│ │ ├── Plugins.md
│ │ ├── Settings.md
│ │ └── Tooling.md
│ ├── FAQ.md
│ ├── From-Nodes-To-Code/
│ │ └── From-Nodes-To-Code.md
│ ├── Setup-BlenderMalt-for-Development.md
│ ├── extra/
│ │ ├── extra.css
│ │ └── extra.js
│ ├── index.md
│ ├── overrides/
│ │ └── partials/
│ │ └── _footer.html
│ └── reference/
│ ├── Light-graph.md
│ ├── Mesh-graph.md
│ ├── Render Layer-graph.md
│ ├── Render-graph.md
│ ├── Screen-graph.md
│ └── settings.md
├── mkdocs/
│ ├── mkdocs.yml
│ ├── netlify.toml
│ └── requirements.txt
├── plugins/
│ ├── Experimental/
│ │ ├── RenderLayer/
│ │ │ └── OpaqueLayer.py
│ │ └── __init__.py
│ └── PluginExample/
│ ├── Shaders/
│ │ └── PluginExample.glsl
│ └── __init__.py
└── scripts/
├── PatchDependencies/
│ ├── mcpp-Darwin
│ └── mcpp-Linux
├── build_intellisense_glsl.py
├── format.py
├── generate_conversion_nodes.py
├── generate_license_dependencies_full.py
├── get_glslang.py
├── install_dependencies.py
├── print_pixel_formats.py
├── settings.json
└── setup_blender_addon.py
SYMBOL INDEX (1014 symbols across 77 files)
FILE: BlenderMalt/CBlenderMalt/CBlenderMalt.cpp
function EXPORT (line 10) | EXPORT void retrieve_mesh_data(
function EXPORT (line 36) | EXPORT bool mesh_tangents(
FILE: BlenderMalt/CBlenderMalt/mikktspace.c
type SVec3 (line 43) | typedef struct {
function tbool (line 47) | static tbool veq( const SVec3 v1, const SVec3 v2 )
function SVec3 (line 52) | static SVec3 vadd( const SVec3 v1, const SVec3 v2 )
function SVec3 (line 64) | static SVec3 vsub( const SVec3 v1, const SVec3 v2 )
function SVec3 (line 75) | static SVec3 vscale(const float fS, const SVec3 v)
function LengthSquared (line 86) | static float LengthSquared( const SVec3 v )
function Length (line 91) | static float Length( const SVec3 v )
function SVec3 (line 96) | static SVec3 Normalize( const SVec3 v )
function vdot (line 101) | static float vdot( const SVec3 v1, const SVec3 v2)
function tbool (line 107) | static tbool NotZero(const float fX)
function tbool (line 113) | static tbool VNotZero(const SVec3 v)
type SSubGroup (line 121) | typedef struct {
type SGroup (line 126) | typedef struct {
type STriInfo (line 141) | typedef struct {
type STSpace (line 155) | typedef struct {
function MakeIndex (line 172) | static int MakeIndex(const int iFace, const int iVert)
function IndexToData (line 178) | static void IndexToData(int * piFace, int * piVert, const int iIndexIn)
function STSpace (line 184) | static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1)
function tbool (line 224) | tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext)
function tbool (line 229) | tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAng...
type STmpVert (line 424) | typedef struct {
function NOINLINE (line 440) | static NOINLINE int FindGridCell(const float fMin, const float fMax, con...
function GenerateSharedVerticesIndexList (line 451) | static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], ...
function MergeVertsFast (line 578) | static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert...
function MergeVertsSlow (line 694) | static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpace...
function GenerateSharedVerticesIndexListSlow (line 729) | static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out...
function GenerateInitialVerticesIndexList (line 772) | static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int pi...
function SVec3 (line 883) | static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int ...
function SVec3 (line 893) | static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int in...
function SVec3 (line 903) | static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int ...
type SEdge (line 916) | typedef union {
function CalcTexArea (line 928) | static float CalcTexArea(const SMikkTSpaceContext * pContext, const int ...
function InitTriInfo (line 944) | static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], c...
function Build4RuleGroups (line 1069) | static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int ...
function AddTriToGroup (line 1133) | static void AddTriToGroup(SGroup * pGroup, const int iTriIndex)
function tbool (line 1139) | static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[],
function tbool (line 1198) | static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfo...
function STSpace (line 1367) | static STSpace EvalTspace(int face_indices[], const int iFaces, const in...
function tbool (line 1441) | static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2)
function QuickSort (line 1454) | static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned ...
function BuildNeighborsFast (line 1501) | static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, con...
function BuildNeighborsSlow (line 1596) | static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriList...
function QuickSortEdges (line 1645) | static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, c...
function GetEdge (line 1703) | static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const...
function DegenPrologue (line 1737) | static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], con...
function DegenEpilogue (line 1820) | static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int ...
FILE: BlenderMalt/CBlenderMalt/mikktspace.h
type tbool (line 62) | typedef int tbool;
type SMikkTSpaceContext (line 63) | typedef struct SMikkTSpaceContext SMikkTSpaceContext;
type SMikkTSpaceInterface (line 65) | typedef struct {
type SMikkTSpaceContext (line 106) | struct SMikkTSpaceContext
FILE: BlenderMalt/MaltLights.py
class MaltLight (line 4) | class MaltLight(bpy.types.PropertyGroup):
method sync_data (line 6) | def sync_data(self, context):
method draw_ui (line 50) | def draw_ui(self, layout):
function register (line 67) | def register():
function unregister (line 72) | def unregister():
FILE: BlenderMalt/MaltMaterial.py
class MaltMaterial (line 8) | class MaltMaterial(bpy.types.PropertyGroup):
method update_source (line 10) | def update_source(self, context):
method update_nodes (line 20) | def update_nodes(self, context):
method poll_tree (line 24) | def poll_tree(self, object):
method get_source_path (line 42) | def get_source_path(self):
method draw_ui (line 54) | def draw_ui(self, layout, extension, material_parameters):
class MALT_PT_MaterialSettings (line 98) | class MALT_PT_MaterialSettings(bpy.types.Panel):
method poll (line 107) | def poll(cls, context):
method draw (line 110) | def draw(self, context):
function reset_materials (line 184) | def reset_materials():
function track_shader_changes (line 192) | def track_shader_changes(force_update=False, async_compilation=True):
function register (line 244) | def register():
function unregister (line 251) | def unregister():
FILE: BlenderMalt/MaltMeshes.py
function get_mesh_name (line 6) | def get_mesh_name(object):
function get_mesh (line 12) | def get_mesh(object):
function load_mesh (line 18) | def load_mesh(object, name):
function get_load_buffer (line 121) | def get_load_buffer(name, ctype, size):
function unload_mesh (line 125) | def unload_mesh(object):
function reset_meshes (line 128) | def reset_meshes():
function draw_vertex_color_overrides (line 132) | def draw_vertex_color_overrides(self, context):
function register (line 152) | def register():
function unregister (line 164) | def unregister():
FILE: BlenderMalt/MaltNodes/MaltCustomPasses.py
class MaltIOParameter (line 4) | class MaltIOParameter(bpy.types.PropertyGroup):
method get_parameter_enums (line 6) | def get_parameter_enums(self, context=None):
method get_parameter (line 18) | def get_parameter(self):
method set_parameter (line 24) | def set_parameter(self, value):
method draw (line 36) | def draw(self, context, layout, owner):
class MaltCustomIO (line 41) | class MaltCustomIO(bpy.types.PropertyGroup):
class MaltCustomPasses (line 53) | class MaltCustomPasses(bpy.types.PropertyGroup):
class MaltGraphType (line 59) | class MaltGraphType(bpy.types.PropertyGroup):
function setup_default_passes (line 65) | def setup_default_passes(graphs, world=None):
class OT_MaltAddCustomPass (line 96) | class OT_MaltAddCustomPass(bpy.types.Operator):
method draw (line 104) | def draw(self, context):
method execute (line 107) | def execute(self, context):
method invoke (line 114) | def invoke(self, context, event):
function register (line 126) | def register():
function unregister (line 132) | def unregister():
FILE: BlenderMalt/MaltNodes/MaltNode.py
class MaltNode (line 7) | class MaltNode():
method get_parameters (line 31) | def get_parameters(self, overrides, resources):
method _disable_updates_wrapper (line 45) | def _disable_updates_wrapper(self, function):
method init (line 58) | def init(self, context):
method setup (line 61) | def setup(self, context=None):
method setup_implementation (line 64) | def setup_implementation(self, copy=None):
method update (line 77) | def update(self):
method copy (line 82) | def copy(self, node):
method free (line 98) | def free(self):
method malt_init (line 103) | def malt_init(self):
method malt_setup (line 106) | def malt_setup(self, copy=None):
method malt_update (line 109) | def malt_update(self):
method on_socket_update (line 112) | def on_socket_update(self, socket):
method setup_sockets (line 115) | def setup_sockets(self, inputs, outputs, expand_structs=True, show_in_...
method get_input_parameter_name (line 239) | def get_input_parameter_name(self, key):
method should_delete_outdated_links (line 252) | def should_delete_outdated_links(self):
method calc_node_width (line 255) | def calc_node_width(self, point_size) -> float:
method setup_width (line 276) | def setup_width(self):
method get_source_name (line 280) | def get_source_name(self):
method get_source_code (line 283) | def get_source_code(self, transpiler):
method get_source_socket_reference (line 289) | def get_source_socket_reference(self, socket):
method sockets_to_global_parameters (line 295) | def sockets_to_global_parameters(self, sockets, transpiler):
method get_source_global_parameters (line 304) | def get_source_global_parameters(self, transpiler):
method setup_socket_shapes (line 307) | def setup_socket_shapes(self):
method is_column_type (line 312) | def is_column_type(self, data_type):
method draw_socket (line 319) | def draw_socket(self, context, layout, socket, text):
method poll (line 343) | def poll(cls, ntree):
method draw_label (line 346) | def draw_label(self):
function register (line 361) | def register():
function unregister (line 364) | def unregister():
FILE: BlenderMalt/MaltNodes/MaltNodeTree.py
function get_pipeline_graph (line 10) | def get_pipeline_graph(context):
class MaltTree (line 15) | class MaltTree(bpy.types.NodeTree):
method get_copy (line 23) | def get_copy(self):
method poll (line 38) | def poll(cls, context):
method poll_material (line 41) | def poll_material(self, material):
method update_graph_type (line 44) | def update_graph_type(self, context):
method is_active (line 84) | def is_active(self):
method get_source_language (line 87) | def get_source_language(self):
method get_transpiler (line 90) | def get_transpiler(self):
method get_library_path (line 96) | def get_library_path(self):
method get_library (line 104) | def get_library(self):
method get_full_library (line 111) | def get_full_library(self):
method get_pipeline_graph (line 130) | def get_pipeline_graph(self, graph_type=None):
method get_unique_node_id (line 138) | def get_unique_node_id(self, base_name):
method get_custom_io (line 148) | def get_custom_io(self, io_type):
method cast (line 162) | def cast(self, from_type, to_type):
method get_struct_type (line 171) | def get_struct_type(self, struct_type):
method get_generated_source_dir (line 177) | def get_generated_source_dir(self):
method get_generated_source_path (line 184) | def get_generated_source_path(self):
method get_generated_source (line 196) | def get_generated_source(self, force_update=False):
method reload_nodes (line 248) | def reload_nodes(self):
method update (line 262) | def update(self):
method update_ext (line 266) | def update_ext(self, force_track_shader_changes=True, force_update=Fal...
function setup_node_trees (line 326) | def setup_node_trees():
function manual_skip_save (line 342) | def manual_skip_save():
function get_libraries (line 352) | def get_libraries():
function get_empty_library (line 354) | def get_empty_library():
function track_library_changes (line 363) | def track_library_changes(force_update=False, is_initial_setup=False):
class NODE_PT_MaltNodeTree (line 432) | class NODE_PT_MaltNodeTree(bpy.types.Panel):
method poll (line 440) | def poll(cls, context):
method draw (line 443) | def draw(self, context):
function preload_menus (line 448) | def preload_menus(structs, functions, graph=None):
function node_header_ui (line 630) | def node_header_ui(self, context):
function get_node_spaces (line 643) | def get_node_spaces(context):
function set_node_tree (line 658) | def set_node_tree(context, node_tree, node = None):
function active_material_update (line 669) | def active_material_update(dummy=None):
function depsgraph_update (line 684) | def depsgraph_update(scene, depsgraph):
function load_post (line 698) | def load_post(dummy=None):
function register (line 714) | def register():
function unregister (line 724) | def unregister():
FILE: BlenderMalt/MaltNodes/MaltNodeUITools.py
class NodeTreePreview (line 11) | class NodeTreePreview(bpy.types.PropertyGroup):
method node_tree (line 23) | def node_tree(self) -> NodeTree:
method is_socket_valid (line 26) | def is_socket_valid(self, socket: bpy.types.NodeSocket) -> bool:
method get_node (line 32) | def get_node(self) -> Union[bpy.types.Node, None]:
method get_socket_ex (line 35) | def get_socket_ex(self) -> tuple[Union[bpy.types.NodeSocket, None], Un...
method get_socket (line 46) | def get_socket(self) -> Union[bpy.types.NodeSocket, None]:
method _get_visible_node_sockets (line 50) | def _get_visible_node_sockets(node: bpy.types.Node, get_outputs: bool ...
method set_socket (line 54) | def set_socket(self, socket: bpy.types.NodeSocket) -> bool:
method reset_socket_from_node (line 64) | def reset_socket_from_node(self, node: bpy.types.Node) -> bool:
method connect_socket (line 76) | def connect_socket(self, socket: bpy.types.NodeSocket) -> bool:
method reconnect_node (line 82) | def reconnect_node(self, node: bpy.types.Node) -> bool:
function is_malt_tree_context (line 100) | def is_malt_tree_context(context: bpy.types.Context) -> bool:
function is_malt_node_context (line 104) | def is_malt_node_context(context: bpy.types.Context) -> bool:
class OT_MaltEditNodeTree (line 107) | class OT_MaltEditNodeTree(bpy.types.Operator):
method poll (line 113) | def poll( cls, context ):
method execute (line 116) | def execute( self, context ):
class OT_MaltSetTreePreview (line 128) | class OT_MaltSetTreePreview(bpy.types.Operator):
method poll (line 135) | def poll(cls, context):
method execute (line 138) | def execute(self, context):
class OT_MaltConnectTreePreview (line 147) | class OT_MaltConnectTreePreview(bpy.types.Operator):
method poll (line 154) | def poll(cls, context):
method execute (line 157) | def execute(self, context):
class OT_MaltCycleSubCategories (line 167) | class OT_MaltCycleSubCategories(bpy.types.Operator):
method poll (line 174) | def poll(cls, context):
method invoke (line 177) | def invoke(self, context: bpy.types.Context, event: bpy.types.Event):
method register_interface (line 190) | def register_interface(self, register: bool) -> None:
method reset_ui_lists (line 199) | def reset_ui_lists(self):
method cycle_function_enums (line 204) | def cycle_function_enums(self, letter: str, cycle_forward: bool) -> None:
method modal (line 226) | def modal(self, context: bpy.types.Context, event: bpy.types.Event):
method execute (line 238) | def execute(self, context: bpy.types.Context):
method cancel (line 246) | def cancel(self, context):
method draw_modal_interface (line 254) | def draw_modal_interface(operator: 'OT_MaltCycleSubCategories') -> None:
class NODE_OT_add_malt_subcategory_node (line 318) | class NODE_OT_add_malt_subcategory_node(bpy.types.Operator):
method poll (line 328) | def poll(cls, context: bpy.types.Context):
method execute (line 331) | def execute(self, context: bpy.types.Context):
class MaltNodeDrawCallbacks (line 356) | class MaltNodeDrawCallbacks:
method get_dpifac (line 359) | def get_dpifac(context: bpy.types.Context):
method real_region_loc (line 364) | def real_region_loc(view_location: Vector|tuple|list, context: bpy.typ...
method get_view_zoom (line 373) | def get_view_zoom(context: bpy.types.Context) -> float:
method context_path_ui_callback (line 378) | def context_path_ui_callback():
method tree_preview_ui_callback (line 400) | def tree_preview_ui_callback():
function register_node_tree_shortcuts (line 435) | def register_node_tree_shortcuts():
function register (line 449) | def register():
function unregister (line 462) | def unregister():
FILE: BlenderMalt/MaltNodes/MaltSocket.py
function get_type_color (line 22) | def get_type_color(type):
class MaltSocket (line 31) | class MaltSocket(bpy.types.NodeSocket):
method on_type_update (line 35) | def on_type_update(self, context):
method show_in_material_panel_update (line 47) | def show_in_material_panel_update(self, context=None):
method is_instantiable_type (line 61) | def is_instantiable_type(self):
method get_source_reference (line 64) | def get_source_reference(self, target_type=None):
method get_source_global_reference (line 75) | def get_source_global_reference(self):
method is_struct_member (line 85) | def is_struct_member(self):
method get_struct_socket (line 88) | def get_struct_socket(self):
method get_source_initialization (line 97) | def get_source_initialization(self):
method get_linked (line 108) | def get_linked(self, ignore_muted=True):
method get_ui_label (line 127) | def get_ui_label(self, print_type=True):
method draw (line 141) | def draw(self, context, layout, node, text):
method setup_shape (line 153) | def setup_shape(self):
method draw_color (line 172) | def draw_color(self, context, node):
function register (line 184) | def register():
function unregister (line 187) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltArrayIndexNode.py
class MaltArrayIndexNode (line 6) | class MaltArrayIndexNode(bpy.types.Node, MaltNode):
method malt_init (line 10) | def malt_init(self):
method malt_setup (line 13) | def malt_setup(self, copy=None):
method malt_update (line 17) | def malt_update(self):
method get_source_socket_reference (line 32) | def get_source_socket_reference(self, socket):
method get_source_code (line 35) | def get_source_code(self, transpiler):
function register (line 50) | def register():
function unregister (line 53) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltFunctionNode.py
class MaltFunctionNodeBase (line 6) | class MaltFunctionNodeBase(MaltNode):
method malt_setup (line 8) | def malt_setup(self, copy=None):
method get_parameters (line 53) | def get_parameters(self, overrides, resources):
method find_replacement_function (line 64) | def find_replacement_function(self):
method get_function (line 101) | def get_function(self, skip_overrides=True, find_replacement=False):
method get_pass_type (line 118) | def get_pass_type(self):
method get_pass_node_tree (line 126) | def get_pass_node_tree(self):
method get_custom_io (line 137) | def get_custom_io(self):
method get_source_socket_reference (line 176) | def get_source_socket_reference(self, socket):
method get_source_code (line 184) | def get_source_code(self, transpiler):
method draw_buttons (line 219) | def draw_buttons(self, context, layout):
class MaltFunctionNode (line 230) | class MaltFunctionNode(bpy.types.Node, MaltFunctionNodeBase):
function register (line 237) | def register():
function unregister (line 240) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltFunctionSubCategory.py
class MaltFunctionSubCategoryNode (line 4) | class MaltFunctionSubCategoryNode(bpy.types.Node, MaltFunctionNodeBase):
method get_function_enums (line 10) | def get_function_enums(self, context=None):
method update_function_enum (line 23) | def update_function_enum(self, context=None):
method malt_setup (line 31) | def malt_setup(self, copy=None):
method should_delete_outdated_links (line 42) | def should_delete_outdated_links(self):
method draw_buttons (line 45) | def draw_buttons(self, context, layout):
method calc_node_width (line 53) | def calc_node_width(self, point_size) -> float:
method draw_label (line 61) | def draw_label(self):
function register (line 68) | def register():
function unregister (line 71) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltIONode.py
class MaltIONode (line 7) | class MaltIONode(bpy.types.Node, MaltNode):
method get_custom_pass_enums (line 16) | def get_custom_pass_enums(self, context):
method malt_setup (line 29) | def malt_setup(self, copy=None):
method get_function (line 71) | def get_function(self):
method get_custom_pass_io (line 75) | def get_custom_pass_io(self):
method get_custom_parameters (line 80) | def get_custom_parameters(self):
method get_dynamic_parameter_types (line 90) | def get_dynamic_parameter_types(self):
method is_custom_socket (line 97) | def is_custom_socket(self, socket):
method get_source_socket_reference (line 106) | def get_source_socket_reference(self, socket):
method get_source_code (line 114) | def get_source_code(self, transpiler):
method get_source_global_parameters (line 140) | def get_source_global_parameters(self, transpiler):
method draw_buttons (line 159) | def draw_buttons(self, context, layout):
method draw_buttons_ext (line 172) | def draw_buttons_ext(self, context, layout):
function register (line 219) | def register():
function unregister (line 222) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltInlineNode.py
class MaltInlineNode (line 5) | class MaltInlineNode(bpy.types.Node, MaltNode):
method code_update (line 9) | def code_update(self, context):
method on_socket_update (line 16) | def on_socket_update(self, socket):
method malt_init (line 20) | def malt_init(self):
method malt_update (line 23) | def malt_update(self):
method draw_buttons (line 49) | def draw_buttons(self, context, layout):
method draw_socket (line 52) | def draw_socket(self, context, layout, socket, text):
method get_source_socket_reference (line 59) | def get_source_socket_reference(self, socket):
method get_source_code (line 62) | def get_source_code(self, transpiler):
function register (line 82) | def register():
function unregister (line 85) | def unregister():
FILE: BlenderMalt/MaltNodes/Nodes/MaltStructNode.py
class MaltStructNode (line 5) | class MaltStructNode(bpy.types.Node, MaltNode):
method malt_setup (line 9) | def malt_setup(self, copy=None):
method get_struct (line 20) | def get_struct(self):
method get_source_socket_reference (line 27) | def get_source_socket_reference(self, socket):
method struct_input_is_linked (line 33) | def struct_input_is_linked(self):
method get_source_code (line 36) | def get_source_code(self, transpiler):
function register (line 54) | def register():
function unregister (line 57) | def unregister():
FILE: BlenderMalt/MaltNodes/_init_.py
function get_modules (line 1) | def get_modules():
function register (line 25) | def register():
function unregister (line 33) | def unregister():
FILE: BlenderMalt/MaltPipeline.py
function is_malt_active (line 10) | def is_malt_active():
function get_bridge (line 18) | def get_bridge(world=None, force_creation=False):
function sync_pipeline_settings (line 30) | def sync_pipeline_settings(default_world=None):
class MaltPipeline (line 47) | class MaltPipeline(bpy.types.PropertyGroup):
method update_pipeline (line 49) | def update_pipeline(self, context):
method update_pipeline_settings (line 95) | def update_pipeline_settings(self, context):
method draw_ui (line 127) | def draw_ui(self, layout):
class OT_MaltReloadPipeline (line 137) | class OT_MaltReloadPipeline(bpy.types.Operator):
method poll (line 142) | def poll(cls, context):
method execute (line 145) | def execute(self, context):
class MALT_PT_Pipeline (line 152) | class MALT_PT_Pipeline(bpy.types.Panel):
method poll (line 161) | def poll(cls, context):
method draw (line 164) | def draw(self, context):
function setup_all_ids (line 173) | def setup_all_ids():
function setup_parameters (line 187) | def setup_parameters(ids):
function depsgraph_update (line 227) | def depsgraph_update(scene, depsgraph):
function load_scene (line 293) | def load_scene(dummy1=None,dummy2=None):
function load_scene_post (line 298) | def load_scene_post(dummy1=None,dummy2=None):
function save_pre (line 306) | def save_pre(dummy1=None,dummy2=None):
function save_post (line 311) | def save_post(dummy1=None,dummy2=None):
function track_pipeline_changes (line 315) | def track_pipeline_changes():
function register (line 332) | def register():
function unregister (line 343) | def unregister():
FILE: BlenderMalt/MaltProperties.py
class MaltBoolPropertyWrapper (line 7) | class MaltBoolPropertyWrapper(bpy.types.PropertyGroup):
class MaltEnumPropertyWrapper (line 11) | class MaltEnumPropertyWrapper(bpy.types.PropertyGroup):
method get_items (line 15) | def get_items(self, context=None):
class MaltGradientPropertyWrapper (line 24) | class MaltGradientPropertyWrapper(bpy.types.PropertyGroup):
method poll (line 25) | def poll(self, texture):
method add_or_duplicate (line 30) | def add_or_duplicate(self, name=None):
class MaltTexturePropertyWrapper (line 45) | class MaltTexturePropertyWrapper(bpy.types.PropertyGroup):
class MaltMaterialPropertyWrapper (line 49) | class MaltMaterialPropertyWrapper(bpy.types.PropertyGroup):
method poll (line 50) | def poll(self, material):
method add_or_duplicate (line 59) | def add_or_duplicate(self, name=None):
class MaltGraphPropertyWrapper (line 70) | class MaltGraphPropertyWrapper(bpy.types.PropertyGroup):
method poll (line 71) | def poll(self, tree):
method add_or_duplicate (line 78) | def add_or_duplicate(self, name=None):
class MaltPropertyGroup (line 89) | class MaltPropertyGroup(bpy.types.PropertyGroup):
method get_rna (line 119) | def get_rna(self):
method setup (line 132) | def setup(self, parameters, replace_parameters=True, reset_to_defaults...
method rename_property (line 390) | def rename_property(self, old_name, new_name):
method remove_property (line 412) | def remove_property(self, name):
method add_override (line 436) | def add_override(self, property_name, override_name):
method remove_override (line 459) | def remove_override(self, property):
method handle_duplication (line 465) | def handle_duplication(self):
method get_parameters (line 469) | def get_parameters(self, overrides, proxys):
method get_parameter (line 482) | def get_parameter(self, key, overrides, proxys, retrieve_blender_type=...
method draw_ui (line 568) | def draw_ui(self, layout, filter=None):
method draw_parameter (line 653) | def draw_parameter(self, layout, key, label, draw_callback=None, is_no...
class OT_MaltNewOverride (line 763) | class OT_MaltNewOverride(bpy.types.Operator):
method get_override_enums (line 768) | def get_override_enums(self, context):
method invoke (line 780) | def invoke(self, context, event):
method draw (line 783) | def draw(self, context):
method execute (line 787) | def execute(self, context):
class MALT_PT_Base (line 792) | class MALT_PT_Base(bpy.types.Panel):
method get_malt_property_owner (line 801) | def get_malt_property_owner(cls, context):
method get_parameter_type (line 805) | def get_parameter_type(cls):
method poll (line 809) | def poll(cls, context):
method draw (line 818) | def draw(self, context):
class MALT_PT_Scene (line 825) | class MALT_PT_Scene(MALT_PT_Base):
method get_parameter_type (line 828) | def get_parameter_type(cls):
method get_malt_property_owner (line 831) | def get_malt_property_owner(cls, context):
class MALT_PT_World (line 834) | class MALT_PT_World(MALT_PT_Base):
method get_parameter_type (line 837) | def get_parameter_type(cls):
method get_malt_property_owner (line 840) | def get_malt_property_owner(cls, context):
class MALT_PT_Camera (line 843) | class MALT_PT_Camera(MALT_PT_Base):
method get_parameter_type (line 846) | def get_parameter_type(cls):
method get_malt_property_owner (line 849) | def get_malt_property_owner(cls, context):
class MALT_PT_Object (line 852) | class MALT_PT_Object(MALT_PT_Base):
method get_parameter_type (line 855) | def get_parameter_type(cls):
method get_malt_property_owner (line 858) | def get_malt_property_owner(cls, context):
class MALT_PT_Mesh (line 871) | class MALT_PT_Mesh(MALT_PT_Base):
method get_parameter_type (line 874) | def get_parameter_type(cls):
method get_malt_property_owner (line 877) | def get_malt_property_owner(cls, context):
class MALT_PT_Light (line 887) | class MALT_PT_Light(MALT_PT_Base):
method get_malt_property_owner (line 890) | def get_malt_property_owner(cls, context):
method draw (line 893) | def draw(self, context):
function register (line 927) | def register():
function unregister (line 950) | def unregister():
FILE: BlenderMalt/MaltRenderEngine.py
function high_res_sleep (line 20) | def high_res_sleep(seconds):
class MaltRenderEngine (line 29) | class MaltRenderEngine(bpy.types.RenderEngine):
method __init__ (line 36) | def __init__(self, *args, **kwargs):
method __del__ (line 48) | def __del__(self):
method get_scene (line 61) | def get_scene(self, context, depsgraph, request_scene_update, overrides):
method get_AOVs (line 221) | def get_AOVs(self, scene):
method update_render_passes (line 234) | def update_render_passes(self, scene=None, renderlayer=None):
method render (line 247) | def render(self, depsgraph):
method view_update (line 299) | def view_update(self, context, depsgraph):
method view_draw (line 308) | def view_draw(self, context, depsgraph):
class DisplayDrawGL (line 416) | class DisplayDrawGL():
method __init__ (line 417) | def __init__(self):
method draw (line 434) | def draw(self, fbo, texture):
class DisplayDrawGPU (line 442) | class DisplayDrawGPU():
method __init__ (line 443) | def __init__(self):
method draw (line 537) | def draw(self, bit_depth, resolution, texture):
class OT_MaltRenderDocCapture (line 545) | class OT_MaltRenderDocCapture(bpy.types.Operator):
method execute (line 549) | def execute(self, context):
class VIEW3D_PT_Malt_Stats (line 555) | class VIEW3D_PT_Malt_Stats(bpy.types.Panel):
method poll (line 562) | def poll(cls, context):
method draw (line 565) | def draw(self, context):
function get_panels (line 578) | def get_panels():
function register (line 593) | def register():
function unregister (line 600) | def unregister():
FILE: BlenderMalt/MaltTextures.py
function get_texture (line 6) | def get_texture(texture):
function __load_texture (line 13) | def __load_texture(texture):
function add_gradient_workaround (line 34) | def add_gradient_workaround(texture):
function get_gradient (line 37) | def get_gradient(texture):
function __load_gradient (line 43) | def __load_gradient(texture):
function copy_color_ramp (line 53) | def copy_color_ramp(old, new):
function reset_textures (line 65) | def reset_textures():
function unload_texture (line 71) | def unload_texture(texture):
function unload_gradients (line 74) | def unload_gradients(texture):
function register (line 79) | def register():
function unregister (line 82) | def unregister():
FILE: BlenderMalt/MaltUtils.py
class OT_MaltPrintError (line 3) | class OT_MaltPrintError(bpy.types.Operator):
method description (line 12) | def description(cls, context, properties):
method execute (line 15) | def execute(self, context):
method modal (line 19) | def modal(self, context, event):
method invoke (line 23) | def invoke(self, context, event):
function malt_path_set_transform (line 28) | def malt_path_set_transform(self, new_value, curr_value, is_set):
function malt_path_get_transform (line 31) | def malt_path_get_transform(self, curr_value, is_set):
class MaltCallback (line 42) | class MaltCallback(bpy.types.PropertyGroup):
method set (line 44) | def set(self, callback, message=''):
method call (line 52) | def call(self, *args, **kwargs):
class OT_MaltCallback (line 56) | class OT_MaltCallback(bpy.types.Operator):
method description (line 64) | def description(self, context, properties):
method execute (line 67) | def execute(self, context):
class COMMON_UL_UI_List (line 71) | class COMMON_UL_UI_List(bpy.types.UIList):
method draw_item (line 73) | def draw_item(self, context, layout, data, item, icon, active_data, ac...
function malt_template_list (line 76) | def malt_template_list(layout, owner, list_property, index_property, add...
function to_json_rna_path_node_workaround (line 91) | def to_json_rna_path_node_workaround(malt_property_group, path_from_group):
function to_json_rna_path (line 99) | def to_json_rna_path(prop):
function from_json_rna_path (line 108) | def from_json_rna_path(prop):
function register (line 132) | def register():
function unregister (line 135) | def unregister():
FILE: BlenderMalt/__init__.py
class Preferences (line 26) | class Preferences(bpy.types.AddonPreferences):
method update_debug_mode (line 44) | def update_debug_mode(self, context):
method draw (line 56) | def draw(self, context):
function draw_node_tree_overlays (line 74) | def draw_node_tree_overlays(self:bpy.types.Menu, context: bpy.types.Cont...
function setup_vs_code (line 98) | def setup_vs_code(dummy):
function register_plugins (line 118) | def register_plugins():
function unregister_plugins (line 141) | def unregister_plugins():
class OT_MaltReloadPlugins (line 154) | class OT_MaltReloadPlugins(bpy.types.Operator):
method execute (line 158) | def execute(self, context):
function get_modules (line 164) | def get_modules():
function register (line 174) | def register():
function unregister (line 193) | def unregister():
FILE: Bridge/Client_API.py
function bridge_method (line 4) | def bridge_method(function):
class IOCapture (line 17) | class IOCapture(io.StringIO):
method __init__ (line 18) | def __init__(self, parent, log_path, log_level):
method write (line 24) | def write(self, s):
class Bridge (line 29) | class Bridge():
method __init__ (line 31) | def __init__(self, pipeline_path, viewport_bit_depth=8, debug_mode=Fal...
method __del__ (line 108) | def __del__(self):
method get_parameters (line 113) | def get_parameters(self):
method get_stats (line 117) | def get_stats(self):
method compile_material (line 124) | def compile_material(self, path, search_paths=[], custom_passes=[]):
method compile_materials (line 134) | def compile_materials(self, paths, search_paths=[], async_compilation=...
method receive_async_compilation_materials (line 161) | def receive_async_compilation_materials(self):
method reflect_source_libraries (line 171) | def reflect_source_libraries(self, paths):
method reload_graphs (line 179) | def reload_graphs(self, graph_types):
method get_shared_buffer (line 187) | def get_shared_buffer(self, ctype, size):
method load_mesh (line 212) | def load_mesh(self, name, mesh_data):
method load_texture (line 220) | def load_texture(self, name, buffer, resolution, channels, sRGB):
method load_gradient (line 231) | def load_gradient(self, name, pixels, nearest):
method get_viewport_id (line 240) | def get_viewport_id(self):
method free_viewport_id (line 249) | def free_viewport_id(self, viewport_id):
method render (line 253) | def render(self, viewport_id, resolution, scene, scene_update, renderd...
method render_result (line 304) | def render_result(self, viewport_id):
FILE: Bridge/Docs.py
function build_docs (line 1) | def build_docs(pipeline, docs_path):
FILE: Bridge/Material.py
class Material (line 5) | class Material():
method __init__ (line 7) | def __init__(self, path, pipeline, search_paths=[], custom_passes={}):
function get_shader (line 32) | def get_shader(path, parameters):
FILE: Bridge/Mesh.py
function load_mesh (line 3) | def load_mesh(pipeline, msg):
FILE: Bridge/Proxys.py
class MeshProxy (line 6) | class MeshProxy(Mesh):
method __init__ (line 8) | def __init__(self, name, submesh_index):
method resolve (line 13) | def resolve(self):
method __del__ (line 18) | def __del__(self):
class TextureProxy (line 21) | class TextureProxy(Texture):
method __init__ (line 23) | def __init__(self, name):
method resolve (line 27) | def resolve(self):
method __del__ (line 32) | def __del__(self):
class GradientProxy (line 35) | class GradientProxy(Gradient):
method __init__ (line 37) | def __init__(self, name):
method resolve (line 41) | def resolve(self):
method __del__ (line 46) | def __del__(self):
class MaterialProxy (line 49) | class MaterialProxy(Material):
method __init__ (line 51) | def __init__(self, path, shader_parameters, parameters):
method resolve (line 56) | def resolve(self):
FILE: Bridge/Server.py
function log_system_info (line 19) | def log_system_info():
class PBO (line 68) | class PBO():
method __init__ (line 70) | def __init__(self):
method __del__ (line 76) | def __del__(self):
method setup (line 79) | def setup(self, texture, buffer):
method poll (line 104) | def poll(self):
method load (line 108) | def load(self):
class Viewport (line 121) | class Viewport():
method __init__ (line 123) | def __init__(self, pipeline, is_final_render, bit_depth):
method get_print_stats (line 145) | def get_print_stats(self):
method setup (line 155) | def setup(self, new_buffers, resolution, scene, scene_update, renderdo...
method to_srgb (line 207) | def to_srgb(self, texture, target):
method ensure_correct_format (line 214) | def ensure_correct_format(self, key, texture):
method render (line 227) | def render(self):
function gl_debug_callback (line 277) | def gl_debug_callback(source, type, id, severity, length, message, user_...
function main (line 291) | def main(pipeline_path, viewport_bit_depth, connection_addresses,
FILE: Bridge/Texture.py
function load_texture (line 6) | def load_texture(msg):
function load_gradient (line 40) | def load_gradient(name, pixels, nearest):
FILE: Bridge/__init__.py
function reload (line 1) | def reload():
function start_server (line 7) | def start_server(pipeline_path, viewport_bit_depth, connection_addresses,
FILE: Bridge/ipc/__init__.py
class C_SharedMemory (line 11) | class C_SharedMemory(ctypes.Structure):
function errcheck (line 20) | def errcheck(ret, func, args):
class SharedBuffer (line 42) | class SharedBuffer(IBuffer):
method GC (line 47) | def GC(cls):
method __init__ (line 55) | def __init__(self, ctype, size):
method ctype (line 67) | def ctype(self):
method __len__ (line 70) | def __len__(self):
method buffer (line 73) | def buffer(self):
method __getstate__ (line 76) | def __getstate__(self):
method __setstate__ (line 84) | def __setstate__(self, state):
method __del__ (line 92) | def __del__(self):
FILE: Bridge/ipc/ipc.c
function EXPORT (line 10) | EXPORT int create_shared_memory(char* name, size_t size, ipc_sharedmemor...
function EXPORT (line 19) | EXPORT int open_shared_memory(char* name, size_t size, ipc_sharedmemory*...
function EXPORT (line 28) | EXPORT void close_shared_memory(ipc_sharedmemory mem, bool release)
FILE: Bridge/ipc/ipc.h
type ipc_sharedmemory (line 120) | typedef struct ipc_sharedmemory_
type ipc_sharedsemaphore (line 135) | typedef struct ipc_sharedsemaphore_
function ipc_mem_init (line 207) | void ipc_mem_init(ipc_sharedmemory *mem, char *name, size_t size)
function ipc_sem_init (line 225) | void ipc_sem_init(ipc_sharedsemaphore *sem, char *name)
function ipc_mem_open_existing (line 239) | int ipc_mem_open_existing(ipc_sharedmemory *mem)
function ipc_mem_create (line 253) | int ipc_mem_create(ipc_sharedmemory *mem)
function ipc_mem_close (line 269) | void ipc_mem_close(ipc_sharedmemory *mem, bool unlink)
function ipc_sem_create (line 281) | int ipc_sem_create(ipc_sharedsemaphore *sem, int initialvalue)
function ipc_sem_close (line 289) | void ipc_sem_close(ipc_sharedsemaphore *sem)
function ipc_sem_increment (line 296) | void ipc_sem_increment(ipc_sharedsemaphore *sem)
function ipc_sem_decrement (line 301) | void ipc_sem_decrement(ipc_sharedsemaphore *sem)
function ipc_sem_try_decrement (line 306) | int ipc_sem_try_decrement(ipc_sharedsemaphore *sem)
function ipc_mem_open_existing (line 316) | int ipc_mem_open_existing(ipc_sharedmemory *mem)
function ipc_mem_create (line 332) | int ipc_mem_create(ipc_sharedmemory *mem)
function ipc_mem_close (line 355) | void ipc_mem_close(ipc_sharedmemory *mem, bool unlink)
function ipc_sem_create (line 368) | int ipc_sem_create(ipc_sharedsemaphore *sem, int initialvalue)
function ipc_sem_close (line 376) | void ipc_sem_close(ipc_sharedsemaphore *sem)
function ipc_sem_increment (line 383) | void ipc_sem_increment(ipc_sharedsemaphore *sem)
function ipc_sem_decrement (line 388) | void ipc_sem_decrement(ipc_sharedsemaphore *sem)
function ipc_sem_try_decrement (line 393) | int ipc_sem_try_decrement(ipc_sharedsemaphore *sem)
FILE: Bridge/renderdoc/renderdoc_app.h
type RENDERDOC_CaptureOption (line 75) | typedef enum RENDERDOC_CaptureOption {
type RENDERDOC_InputButton (line 235) | typedef enum RENDERDOC_InputButton {
type RENDERDOC_OverlayBits (line 323) | typedef enum RENDERDOC_OverlayBits {
type pRENDERDOC_RemoveHooks (line 362) | typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;
type pRENDERDOC_SetCaptureFilePathTemplate (line 394) | typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathT...
type pRENDERDOC_GetCaptureFilePathTemplate (line 395) | typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathT...
type pRENDERDOC_IsTargetControlConnected (line 435) | typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessCon...
type RENDERDOC_Version (line 540) | typedef enum RENDERDOC_Version {
type RENDERDOC_API_1_4_1 (line 578) | typedef struct RENDERDOC_API_1_4_1
type RENDERDOC_API_1_4_1 (line 652) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_0;
type RENDERDOC_API_1_4_1 (line 653) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_1;
type RENDERDOC_API_1_4_1 (line 654) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_2;
type RENDERDOC_API_1_4_1 (line 655) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_0;
type RENDERDOC_API_1_4_1 (line 656) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_1;
type RENDERDOC_API_1_4_1 (line 657) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_2;
type RENDERDOC_API_1_4_1 (line 658) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_2_0;
type RENDERDOC_API_1_4_1 (line 659) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_3_0;
type RENDERDOC_API_1_4_1 (line 660) | typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_4_0;
FILE: Bridge/renderdoc/renderdoc_wrapper.c
function init (line 14) | void init()
function init (line 27) | void init()
function EXPORT (line 40) | EXPORT void capture_start()
function EXPORT (line 46) | EXPORT void capture_end()
FILE: Malt/GL/GL.py
class GLhalfFloatArray (line 21) | class GLhalfFloatArray(ArrayDatatype, ctypes.POINTER(GLhalfARB)):
class DrawQuery (line 37) | class DrawQuery():
method __init__ (line 39) | def __init__(self, query_type=GL_ANY_SAMPLES_PASSED):
method begin_query (line 43) | def begin_query(self):
method end_query (line 50) | def end_query(self):
method begin_conditional_draw (line 53) | def begin_conditional_draw(self, wait_mode=GL_QUERY_WAIT):
method end_conditional_draw (line 56) | def end_conditional_draw(self):
function gl_buffer (line 60) | def gl_buffer(type, size, data=None):
function buffer_to_string (line 84) | def buffer_to_string(buffer):
FILE: Malt/GL/GLSLEval.py
function glsl_vector (line 1) | def glsl_vector(convert, length, *args):
function _vec2 (line 17) | def _vec2(convert, *args): return glsl_vector(convert, 2, *args)
function _vec3 (line 18) | def _vec3(convert, *args): return glsl_vector(convert, 3, *args)
function _vec4 (line 19) | def _vec4(convert, *args): return glsl_vector(convert, 4, *args)
function vec2 (line 21) | def vec2(*args): return _vec2(float, *args)
function vec3 (line 22) | def vec3(*args): return _vec3(float, *args)
function vec4 (line 23) | def vec4(*args): return _vec4(float, *args)
function ivec2 (line 25) | def ivec2(*args): return _vec2(int, *args)
function ivec3 (line 26) | def ivec3(*args): return _vec3(int, *args)
function ivec4 (line 27) | def ivec4(*args): return _vec4(int, *args)
function uint (line 29) | def uint(n): return max(int(n), 0)
function uvec2 (line 31) | def uvec2(*args): return _vec2(uint, *args)
function uvec3 (line 32) | def uvec3(*args): return _vec3(uint, *args)
function uvec4 (line 33) | def uvec4(*args): return _vec4(uint, *args)
function glsl_eval (line 35) | def glsl_eval(str):
FILE: Malt/GL/GLSLParser/src/main.cpp
type line_comment (line 20) | struct line_comment : seq<STRING("//"), until<eol, any>> {}
type multiline_comment (line 21) | struct multiline_comment : seq<STRING("/*"), until<STRING("*/"), any>> {}
type preprocessor_directive (line 22) | struct preprocessor_directive : seq<STRING("#"), until<eol, any>> {}
type META (line 23) | struct META
type META_GLOBAL (line 24) | struct META_GLOBAL
type _s_ (line 25) | struct _s_ : star<not_at<sor<META, META_GLOBAL>>, sor<line_comment, mult...
type LPAREN (line 27) | struct LPAREN : one<'('> {}
type RPAREN (line 28) | struct RPAREN : one<')'> {}
type LBRACE (line 29) | struct LBRACE : one<'{'> {}
type RBRACE (line 30) | struct RBRACE : one<'}'> {}
type LBRACKET (line 31) | struct LBRACKET : one<'['> {}
type RBRACKET (line 32) | struct RBRACKET : one<']'> {}
type COMMA (line 33) | struct COMMA : one<','> {}
type END (line 34) | struct END : one<';'> {}
type STRUCT (line 35) | struct STRUCT : KEYWORD("struct") {}
type IDENTIFIER (line 36) | struct IDENTIFIER : identifier {}
type TYPE (line 37) | struct TYPE : identifier {}
type DIGITS (line 38) | struct DIGITS : plus<digit> {}
type PRECISION (line 39) | struct PRECISION : sor<KEYWORD("lowp"), KEYWORD("mediump"), KEYWORD("hig...
type IO (line 40) | struct IO : sor<KEYWORD("in"), KEYWORD("out"), KEYWORD("inout")> {}
type ARRAY_SIZE (line 41) | struct ARRAY_SIZE : seq<LBRACKET, _s_, DIGITS, _s_, RBRACKET> {}
type FILE_PATH (line 43) | struct FILE_PATH : plus<not_one<'"'>> {}
type QUOTED_FILE_PATH (line 44) | struct QUOTED_FILE_PATH : seq<one<'"'>, FILE_PATH, one<'"'>> {}
type LINE_DIRECTIVE (line 45) | struct LINE_DIRECTIVE : seq<STRING("#line"), _s_, DIGITS, _s_, QUOTED_FI...
type _ms_ (line 47) | struct _ms_ : star<sor<space, preprocessor_directive>> {}
type META_PROP_VALUE (line 48) | struct META_PROP_VALUE : star<not_at<one<';'>>, any> {}
type META_PROP (line 49) | struct META_PROP : seq<not_at<one<'@'>>, IDENTIFIER, _ms_, one<'='>, _ms...
type META_PROPS (line 50) | struct META_PROPS : plus<seq<_ms_, META_PROP, _ms_>> {}
type META_MEMBER (line 51) | struct META_MEMBER : seq<one<'@'>, _ms_, IDENTIFIER, _ms_, one<':'>, MET...
type META_MEMBERS (line 52) | struct META_MEMBERS : plus<seq<_ms_, META_MEMBER, _ms_>> {}
type META (line 53) | struct META : seq<STRING("/*"), _ms_, STRING("META"), _ms_, META_MEMBERS...
type META_GLOBAL (line 55) | struct META_GLOBAL : seq<STRING("/*"), _ms_, STRING("META GLOBAL"), _ms_...
type MEMBER (line 57) | struct MEMBER : seq<opt<PRECISION>, _s_, TYPE, _s_, IDENTIFIER, _s_, opt...
type MEMBERS (line 58) | struct MEMBERS : plus<seq<_s_, MEMBER, _s_>> {}
type STRUCT_DEF (line 59) | struct STRUCT_DEF : seq<opt<META, _ms_>, STRUCT, _s_, IDENTIFIER, _s_, L...
type PARAMETER (line 61) | struct PARAMETER : seq<opt<IO>, _s_, opt<PRECISION>, _s_, TYPE, _s_, IDE...
type PARAMETERS (line 62) | struct PARAMETERS : list<seq<_s_, PARAMETER, _s_>, seq<_s_, COMMA, _s_>> {}
type FUNCTION_SIG (line 63) | struct FUNCTION_SIG : seq<TYPE, _s_, IDENTIFIER, _s_, LPAREN, _s_, opt<P...
type FUNCTION_DEC (line 64) | struct FUNCTION_DEC : seq<opt<META, _ms_>, FUNCTION_SIG, _s_, LBRACE> {}
type GLSL_GRAMMAR (line 66) | struct GLSL_GRAMMAR : star<sor<LINE_DIRECTIVE, META_GLOBAL, STRUCT_DEF, ...
function print_nodes (line 96) | void print_nodes(parse_tree::node& node)
function get_meta_dict (line 125) | std::map<std::string, rapidjson::Value> get_meta_dict(parse_tree::node* ...
function remove_extra_whitespace (line 155) | std::string remove_extra_whitespace(const std::string& str)
function main (line 177) | int main(int argc, char* argv[])
FILE: Malt/GL/Mesh.py
class Mesh (line 5) | class Mesh():
method __init__ (line 7) | def __init__(self, position, index, normal=None, tangent=None, uvs=[],...
method __load_VAO (line 51) | def __load_VAO(self):
method bind (line 80) | def bind(self):
method draw (line 85) | def draw(self, bind=True):
method __del__ (line 92) | def __del__(self):
class MeshCustomLoad (line 112) | class MeshCustomLoad(Mesh):
method __init__ (line 114) | def __init__(self):
FILE: Malt/GL/RenderTarget.py
class RenderTarget (line 6) | class RenderTarget():
method __init__ (line 8) | def __init__(self, targets=[], depth_stencil=None):
method bind (line 49) | def bind(self):
method clear (line 54) | def clear(self, colors=[], depth=None, stencil=None):
method __del__ (line 82) | def __del__(self):
class TargetBase (line 86) | class TargetBase():
method attach (line 87) | def attach(self, attachment):
class ArrayLayerTarget (line 91) | class ArrayLayerTarget(TargetBase):
method __init__ (line 92) | def __init__(self, texture_array, layer):
method attach (line 100) | def attach(self, attachment):
FILE: Malt/GL/Shader.py
class Shader (line 7) | class Shader():
method __init__ (line 9) | def __init__(self, vertex_source, pixel_source):
method bind (line 40) | def bind(self):
method copy (line 58) | def copy(self):
method __del__ (line 73) | def __del__(self):
class GLUniform (line 78) | class GLUniform():
method __init__ (line 79) | def __init__(self, index, type, value, array_length=1):
method is_sampler (line 88) | def is_sampler(self):
method texture_type (line 91) | def texture_type(self):
method set_value (line 108) | def set_value(self, value):
method set_buffer (line 114) | def set_buffer(self, buffer):
method bind (line 117) | def bind(self, buffer=None):
method copy (line 122) | def copy(self):
class UBO (line 130) | class UBO():
method __init__ (line 134) | def __init__(self):
method load_data (line 140) | def load_data(self, structure):
method bind (line 146) | def bind(self, uniform_block):
method __del__ (line 153) | def __del__(self):
function shader_preprocessor (line 157) | def shader_preprocessor(shader_source, include_directories=[], definitio...
function directive_line_support (line 214) | def directive_line_support():
function fix_line_directive_paths (line 248) | def fix_line_directive_paths(source):
function compile_gl_program (line 275) | def compile_gl_program(vertex, fragment):
function reflect_program_uniforms (line 374) | def reflect_program_uniforms(program):
function uniform_type_to_base_type_and_size (line 431) | def uniform_type_to_base_type_and_size(type):
function uniform_type_set_function (line 459) | def uniform_type_set_function(uniform_type):
function reflect_program_uniform_blocks (line 491) | def reflect_program_uniform_blocks(program):
function glsl_reflection (line 517) | def glsl_reflection(code, root_paths=[]):
function glslang_validator (line 629) | def glslang_validator(source, stage):
FILE: Malt/GL/Texture.py
class Texture (line 5) | class Texture():
method __init__ (line 7) | def __init__(self, resolution, internal_format=GL_RGB32F, data_format ...
method bind (line 38) | def bind(self):
method __del__ (line 41) | def __del__(self):
class TextureArray (line 45) | class TextureArray():
method __init__ (line 47) | def __init__(self, resolution, length, internal_format=GL_RGB32F, data...
method bind (line 70) | def bind(self):
method __del__ (line 73) | def __del__(self):
class CubeMap (line 77) | class CubeMap():
method __init__ (line 79) | def __init__(self, resolution, internal_format=GL_RGB32F, data_format ...
method bind (line 103) | def bind(self):
method __del__ (line 106) | def __del__(self):
class CubeMapArray (line 110) | class CubeMapArray():
method __init__ (line 112) | def __init__(self, resolution, length, internal_format=GL_RGB32F, data...
method bind (line 136) | def bind(self):
method __del__ (line 139) | def __del__(self):
class Gradient (line 143) | class Gradient():
method __init__ (line 145) | def __init__(self, data, resolution, internal_format=GL_RGBA32F, data_...
method bind (line 165) | def bind(self):
method __del__ (line 168) | def __del__(self):
function internal_format_to_data_format (line 172) | def internal_format_to_data_format(internal_format):
function data_format_size (line 185) | def data_format_size(data_format):
function internal_format_to_sampler_type (line 197) | def internal_format_to_sampler_type(internal_format):
function internal_format_to_vector_type (line 207) | def internal_format_to_vector_type(internal_format):
function internal_format_to_format (line 215) | def internal_format_to_format(internal_format):
function format_channels (line 235) | def format_channels(format):
FILE: Malt/Nodes/LineRender.py
class LineRender (line 9) | class LineRender(PipelineNode):
method __init__ (line 15) | def __init__(self, pipeline):
method reflect_inputs (line 20) | def reflect_inputs(cls):
method reflect_outputs (line 36) | def reflect_outputs(cls):
method setup_render_targets (line 41) | def setup_render_targets(self, resolution):
method execute (line 45) | def execute(self, parameters):
FILE: Malt/Nodes/SceneFilter.py
class SceneFilter (line 8) | class SceneFilter(PipelineNode):
method __init__ (line 13) | def __init__(self, pipeline):
method reflect_inputs (line 20) | def reflect_inputs(cls):
method reflect_outputs (line 27) | def reflect_outputs(cls):
method execute (line 33) | def execute(self, parameters):
FILE: Malt/Nodes/SuperSamplingAA.py
class SuperSamplingAA (line 7) | class SuperSamplingAA(PipelineNode):
method __init__ (line 13) | def __init__(self, pipeline):
method reflect_inputs (line 18) | def reflect_inputs(cls):
method reflect_outputs (line 24) | def reflect_outputs(cls):
method setup_render_targets (line 29) | def setup_render_targets(self, resolution):
method execute (line 33) | def execute(self, parameters):
FILE: Malt/Pipeline.py
class Pipeline (line 15) | class Pipeline():
method __init__ (line 22) | def __init__(self, plugins=[]):
method setup_parameters (line 48) | def setup_parameters(self):
method get_parameters (line 73) | def get_parameters(self):
method setup_graphs (line 76) | def setup_graphs(self):
method add_graph (line 79) | def add_graph(self, graph):
method get_graphs (line 84) | def get_graphs(self):
method setup_resources (line 90) | def setup_resources(self):
method get_render_outputs (line 114) | def get_render_outputs(self):
method get_samples (line 120) | def get_samples(self):
method needs_more_samples (line 123) | def needs_more_samples(self):
method setup_render_targets (line 126) | def setup_render_targets(self, resolution):
method find_shader_path (line 129) | def find_shader_path(self, path, search_paths=[]):
method compile_shader_from_source (line 139) | def compile_shader_from_source(self, source, include_paths=[], defines...
method compile_material_from_source (line 144) | def compile_material_from_source(self, material_type, source, include_...
method compile_material (line 147) | def compile_material(self, shader_path, search_paths=[]):
method load_mesh (line 161) | def load_mesh(self, position, indices, normal, tangent=None, uvs=[], c...
method draw_screen_pass (line 241) | def draw_screen_pass(self, shader, target, blend = False):
method blend_texture (line 254) | def blend_texture(self, blend_texture, target, opacity):
method copy_textures (line 261) | def copy_textures(self, target, color_sources=[], depth_source=None):
method build_scene_batches (line 267) | def build_scene_batches(self, objects):
method draw_scene_pass (line 327) | def draw_scene_pass(self, render_target, scene_batches, pass_name=None...
method render (line 396) | def render(self, resolution, scene, is_final_render, is_new_frame):
method do_render (line 416) | def do_render(self, resolution, scene, is_final_render, is_new_frame):
FILE: Malt/PipelineGraph.py
class PipelineGraphIO (line 4) | class PipelineGraphIO():
method __init__ (line 6) | def __init__(self, name, dynamic_input_types = [], dynamic_output_type...
class PipelineGraph (line 16) | class PipelineGraph():
method __init__ (line 22) | def __init__(self, name, language, file_extension, graph_type, graph_i...
method add_library (line 37) | def add_library(self, path):
method needs_reload (line 52) | def needs_reload(self):
method setup_reflection (line 63) | def setup_reflection(self):
method generate_source (line 67) | def generate_source(self, parameters):
method get_serializable_copy (line 70) | def get_serializable_copy(self):
class GLSLGraphIO (line 75) | class GLSLGraphIO(PipelineGraphIO):
method __init__ (line 80) | def __init__(self, name, define = None, io_wrap=None, dynamic_input_ty...
class GLSLPipelineGraph (line 90) | class GLSLPipelineGraph(PipelineGraph):
method __init__ (line 92) | def __init__(self, name, graph_type, default_global_scope, default_sha...
method get_serializable_copy (line 102) | def get_serializable_copy(self):
method name_as_macro (line 107) | def name_as_macro(self, name):
method get_material_define (line 110) | def get_material_define(self):
method preprocess_shader_from_source (line 113) | def preprocess_shader_from_source(self, source, include_paths=[], defi...
method setup_reflection (line 117) | def setup_reflection(self):
method generate_source (line 144) | def generate_source(self, parameters):
method compile_material (line 162) | def compile_material(self, source, include_paths=[]):
class PythonGraphIO (line 178) | class PythonGraphIO(PipelineGraphIO):
method __init__ (line 182) | def __init__(self, name, dynamic_input_types = [], dynamic_output_type...
class PythonPipelineGraph (line 187) | class PythonPipelineGraph(PipelineGraph):
method __init__ (line 189) | def __init__(self, name, graph_io, default_graph_path=None):
method get_serializable_copy (line 195) | def get_serializable_copy(self):
method setup_reflection (line 201) | def setup_reflection(self):
method generate_source (line 222) | def generate_source(self, parameters):
method run_source (line 229) | def run_source(self, pipeline, source, PARAMETERS, IN, OUT):
class MaltGraphExecutionException (line 241) | class MaltGraphExecutionException(Exception):
method __init__ (line 242) | def __init__(self, source, parameters, inputs, outputs):
FILE: Malt/PipelineNode.py
class PipelineNode (line 1) | class PipelineNode():
method __init__ (line 3) | def __init__(self, pipeline):
method get_pass_type (line 7) | def get_pass_type():
method static_reflect (line 11) | def static_reflect(cls, name, inputs, outputs):
method reflect (line 56) | def reflect(cls):
method reflect_inputs (line 60) | def reflect_inputs(cls):
method reflect_outputs (line 64) | def reflect_outputs(cls):
method execute (line 67) | def execute(self, parameters):
FILE: Malt/PipelineParameters.py
class PipelineParameters (line 1) | class PipelineParameters():
method __init__ (line 3) | def __init__(self, scene={}, world={}, camera={}, object={}, material=...
class Type (line 12) | class Type():
method string_list (line 26) | def string_list(cls):
method to_string (line 30) | def to_string(cls, type):
method from_string (line 33) | def from_string(cls, type):
class Parameter (line 36) | class Parameter():
method __init__ (line 37) | def __init__(self, default_value, type, size=1, filter=None, subtype=N...
method type_string (line 45) | def type_string(self):
method from_uniform (line 52) | def from_uniform(cls, uniform):
method from_glsl_type (line 63) | def from_glsl_type(cls, glsl_type, subtype=None, default_value=None):
class MaterialParameter (line 109) | class MaterialParameter(Parameter):
method __init__ (line 110) | def __init__(self, default_path, extension, graph_type=None, filter=No...
class GraphParameter (line 115) | class GraphParameter(Parameter):
method __init__ (line 116) | def __init__(self, default_path, graph_type, filter=None, doc=None):
class EnumParameter (line 120) | class EnumParameter(Parameter):
method __init__ (line 121) | def __init__(self, options, default_option, filter=None, doc=None):
method from_index (line 125) | def from_index(self, index):
class FloatParameter (line 128) | class FloatParameter(Parameter):
method __init__ (line 129) | def __init__(self, default_value, min=None, max=None, size=1, filter=N...
class IntParameter (line 134) | class IntParameter(Parameter):
method __init__ (line 135) | def __init__(self, default_value, min=None, max=None, size=1, filter=N...
function gl_type_to_malt_type (line 140) | def gl_type_to_malt_type(gl_type):
function glsl_type_to_malt_type (line 169) | def glsl_type_to_malt_type(glsl_type):
FILE: Malt/PipelinePlugin.py
class PipelinePlugin (line 3) | class PipelinePlugin():
method poll_pipeline (line 6) | def poll_pipeline(self, pipeline):
method register_pipeline_parameters (line 10) | def register_pipeline_parameters(self, parameters):
method register_pipeline_graphs (line 14) | def register_pipeline_graphs(self):
method register_graph_libraries (line 18) | def register_graph_libraries(self, graphs):
method blendermalt_register (line 22) | def blendermalt_register(self):
method blendermalt_unregister (line 26) | def blendermalt_unregister(self):
method blendermalt_register_nodeitems (line 30) | def blendermalt_register_nodeitems(self, MaltNodeItemClass):
function load_plugins_from_dir (line 34) | def load_plugins_from_dir(dir):
FILE: Malt/Pipelines/MiniPipeline/MiniPipeline.py
class MiniPipeline (line 11) | class MiniPipeline(Pipeline):
method __init__ (line 15) | def __init__(self, plugins=[]):
method compile_material_from_source (line 44) | def compile_material_from_source(self, material_type, source, include_...
method setup_render_targets (line 51) | def setup_render_targets(self, resolution):
method do_render (line 56) | def do_render(self, resolution, scene, is_final_render, is_new_frame):
FILE: Malt/Pipelines/NPR_Pipeline/NPR_LightShaders.py
class NPR_LightShaders (line 13) | class NPR_LightShaders():
method __init__ (line 15) | def __init__(self):
method load (line 27) | def load(self, pipeline, depth_texture, scene):
method shader_callback (line 61) | def shader_callback(self, shader):
FILE: Malt/Pipelines/NPR_Pipeline/NPR_Lighting.py
class NPR_LightsGroupsBuffer (line 8) | class NPR_LightsGroupsBuffer():
method __init__ (line 10) | def __init__(self):
method load (line 18) | def load(self, scene):
method shader_callback (line 29) | def shader_callback(self, shader):
class NPR_ShadowMaps (line 34) | class NPR_ShadowMaps(Lighting.ShadowMaps):
method __init__ (line 36) | def __init__(self):
method setup (line 42) | def setup(self, create_fbos=True):
method clear (line 64) | def clear(self, spot_count, sun_count, point_count):
method shader_callback (line 72) | def shader_callback(self, shader):
class NPR_TransparentShadowMaps (line 78) | class NPR_TransparentShadowMaps(NPR_ShadowMaps):
method __init__ (line 80) | def __init__(self):
method setup (line 86) | def setup(self, create_fbos=True):
method clear (line 108) | def clear(self, spot_count, sun_count, point_count):
method shader_callback (line 116) | def shader_callback(self, shader):
FILE: Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py
class NPR_Pipeline (line 68) | class NPR_Pipeline(Pipeline):
method __init__ (line 70) | def __init__(self, plugins=[]):
method setup_parameters (line 78) | def setup_parameters(self):
method setup_graphs (line 112) | def setup_graphs(self):
method setup_resources (line 246) | def setup_resources(self):
method get_samples (line 253) | def get_samples(self):
method get_sample (line 258) | def get_sample(self, width):
method get_scene_batches (line 264) | def get_scene_batches(self, scene):
method do_render (line 275) | def do_render(self, resolution, scene, is_final_render, is_new_frame):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/Render/RenderLayers.py
class RenderLayers (line 9) | class RenderLayers(PipelineNode):
method __init__ (line 15) | def __init__(self, pipeline):
method get_pass_type (line 23) | def get_pass_type():
method reflect_inputs (line 27) | def reflect_inputs(cls):
method reflect_outputs (line 38) | def reflect_outputs(cls):
method setup_render_targets (line 42) | def setup_render_targets(self, resolution, custom_io):
method blend_transparency (line 57) | def blend_transparency(self, back_textures, front_textures, fbo):
method execute (line 66) | def execute(self, parameters):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py
class SceneLighting (line 9) | class SceneLighting(PipelineNode):
method __init__ (line 14) | def __init__(self, pipeline):
method reflect_inputs (line 23) | def reflect_inputs(cls):
method reflect_outputs (line 52) | def reflect_outputs(cls):
method execute (line 58) | def execute(self, parameters):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/Render/ScreenPass.py
class ScreenPass (line 9) | class ScreenPass(PipelineNode):
method __init__ (line 15) | def __init__(self, pipeline):
method get_pass_type (line 23) | def get_pass_type():
method execute (line 26) | def execute(self, parameters):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/MainPass.py
class MainPass (line 8) | class MainPass(PipelineNode):
method __init__ (line 15) | def __init__(self, pipeline):
method get_pass_type (line 21) | def get_pass_type():
method reflect_inputs (line 25) | def reflect_inputs(cls):
method reflect_outputs (line 33) | def reflect_outputs(cls):
method setup_render_targets (line 37) | def setup_render_targets(self, resolution, t_depth, custom_io):
method execute (line 51) | def execute(self, parameters):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/PrePass.py
class PrePass (line 10) | class PrePass(PipelineNode):
method __init__ (line 17) | def __init__(self, pipeline):
method get_pass_type (line 24) | def get_pass_type():
method reflect_inputs (line 28) | def reflect_inputs(cls):
method reflect_outputs (line 34) | def reflect_outputs(cls):
method setup_render_targets (line 41) | def setup_render_targets(self, resolution, custom_io):
method execute (line 60) | def execute(self, parameters):
FILE: Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/ScreenPass.py
class ScreenPass (line 9) | class ScreenPass(PipelineNode):
method __init__ (line 16) | def __init__(self, pipeline):
method get_pass_type (line 24) | def get_pass_type():
method reflect_inputs (line 28) | def reflect_inputs(cls):
method execute (line 39) | def execute(self, parameters):
FILE: Malt/Render/Common.py
class C_CommonBuffer (line 5) | class C_CommonBuffer(ctypes.Structure):
function bake_sample_offset (line 17) | def bake_sample_offset(projection_matrix, sample_offset, resolution):
class CommonBuffer (line 29) | class CommonBuffer():
method __init__ (line 31) | def __init__(self):
method load (line 35) | def load(self, scene, resolution, sample_offset=(0,0), sample_count=0,...
method bind (line 48) | def bind(self, block):
method shader_callback (line 51) | def shader_callback(self, shader):
FILE: Malt/Render/DepthToCompositeDepth.py
class CompositeDepth (line 11) | class CompositeDepth():
method __init__ (line 13) | def __init__(self):
method render (line 19) | def render(self, pipeline, common_buffer, depth_texture, depth_channel...
FILE: Malt/Render/Lighting.py
function get_lights_buffer (line 17) | def get_lights_buffer():
function get_shadow_maps (line 24) | def get_shadow_maps():
class C_Light (line 33) | class C_Light(ctypes.Structure):
class C_LightsBuffer (line 52) | class C_LightsBuffer(ctypes.Structure):
class ShadowMaps (line 64) | class ShadowMaps():
method __init__ (line 66) | def __init__(self):
method load (line 87) | def load(self, scene, spot_resolution, sun_resolution, point_resolutio...
method setup (line 120) | def setup(self, create_fbos=True):
method clear (line 138) | def clear(self, spot_count, sun_count, point_count):
method shader_callback (line 146) | def shader_callback(self, shader):
class LightsBuffer (line 152) | class LightsBuffer():
method __init__ (line 154) | def __init__(self):
method load (line 161) | def load(
method bind (line 256) | def bind(self, block):
method shader_callback (line 259) | def shader_callback(self, shader):
function flatten_matrix (line 264) | def flatten_matrix(matrix):
function make_projection_matrix (line 269) | def make_projection_matrix(fov, aspect_ratio, near, far, sample_offset, ...
function get_sun_cascades (line 282) | def get_sun_cascades(sun_from_world_matrix, projection_matrix, view_from...
function frustum_corners (line 324) | def frustum_corners(view_from_world_matrix, near, far):
function sun_shadowmap_matrix (line 339) | def sun_shadowmap_matrix(sun_from_world_matrix, view_from_world_matrix, ...
FILE: Malt/Render/Sampling.py
function get_RGSS_samples (line 9) | def get_RGSS_samples(grid_size, width=1.0):
function get_random_samples (line 43) | def get_random_samples(grid_size, width=1.0):
FILE: Malt/Scene.py
class Camera (line 1) | class Camera():
method __init__ (line 3) | def __init__(self, camera_matrix, projection_matrix, parameters={}):
class Material (line 8) | class Material():
method __init__ (line 10) | def __init__(self, shader, parameters={}):
class Mesh (line 14) | class Mesh():
method __init__ (line 16) | def __init__(self, mesh, parameters={}):
class Object (line 20) | class Object():
method __init__ (line 22) | def __init__(self, matrix, mesh, material, parameters={}, mirror_scale...
class Light (line 30) | class Light():
method __init__ (line 32) | def __init__(self):
class Scene (line 44) | class Scene():
method __init__ (line 46) | def __init__(self):
class ShaderResource (line 58) | class ShaderResource():
method shader_callback (line 60) | def shader_callback(self, shader):
class TextureShaderResource (line 63) | class TextureShaderResource():
method __init__ (line 65) | def __init__(self, name, texture):
method shader_callback (line 69) | def shader_callback(self, shader):
FILE: Malt/SourceTranspiler.py
class SourceTranspiler (line 4) | class SourceTranspiler():
method get_source_name (line 7) | def get_source_name(self, name):
method asignment (line 15) | def asignment(self, name, asignment):
method declaration (line 19) | def declaration(self, type, size, name, initialization=None):
method global_reference (line 23) | def global_reference(self, node_name, parameter_name):
method global_declaration (line 27) | def global_declaration(self, type, size, name, initialization=None):
method custom_io_reference (line 31) | def custom_io_reference(self, io, graph_io_type, name):
method preprocessor_wrap (line 35) | def preprocessor_wrap(self, define, content):
method custom_output_declaration (line 39) | def custom_output_declaration(self, type, name, index, graph_io_type):
method parameter_reference (line 43) | def parameter_reference(self, node_name, parameter_name, io_type):
method io_parameter_reference (line 47) | def io_parameter_reference(self, parameter_name, io_type):
method is_instantiable_type (line 51) | def is_instantiable_type(self, type):
method call (line 55) | def call(self, name, parameters=[], full_statement=False):
method result (line 59) | def result(self, result):
method scoped (line 63) | def scoped(self, code):
class GLSLTranspiler (line 66) | class GLSLTranspiler(SourceTranspiler):
method asignment (line 69) | def asignment(self, name, asignment):
method declaration (line 73) | def declaration(self, type, size, name, initialization=None):
method global_reference (line 79) | def global_reference(self, node_name, parameter_name):
method global_declaration (line 83) | def global_declaration(self, type, size, name, initialization=None):
method custom_io_reference (line 90) | def custom_io_reference(self, io, graph_io_type, name):
method preprocessor_wrap (line 94) | def preprocessor_wrap(self, define, content):
method custom_output_declaration (line 104) | def custom_output_declaration(self, type, name, index, graph_io_type):
method parameter_reference (line 108) | def parameter_reference(self, node_name, parameter_name, io_type):
method is_instantiable_type (line 112) | def is_instantiable_type(self, type):
method call (line 116) | def call(self, function, name, parameters=[], post_parameter_initializ...
method result (line 136) | def result(self, result):
method scoped (line 140) | def scoped(self, code):
class PythonTranspiler (line 145) | class PythonTranspiler(SourceTranspiler):
method asignment (line 148) | def asignment(self, name, asignment):
method declaration (line 152) | def declaration(self, type, size, name, initialization=None):
method global_reference (line 157) | def global_reference(self, node_name, parameter_name):
method global_declaration (line 161) | def global_declaration(self, type, size, name, initialization=None):
method custom_io_reference (line 166) | def custom_io_reference(self, io, graph_io_type, name):
method custom_output_declaration (line 170) | def custom_output_declaration(self, type, name, index, graph_io_type):
method parameter_reference (line 174) | def parameter_reference(self, node_name, parameter_name, io_type):
method io_parameter_reference (line 181) | def io_parameter_reference(self, parameter_name, io_type):
method call (line 185) | def call(self, function, name, parameters=[], post_parameter_initializ...
method result (line 205) | def result(self, result):
method scoped (line 209) | def scoped(self, code):
FILE: Malt/Utils.py
class MaltLogger (line 3) | class MaltLogger():
method __init__ (line 5) | def __init__(self):
method log (line 9) | def log(self, level, *args):
method debug (line 23) | def debug(self, *args):
method info (line 26) | def info(self, *args):
method warning (line 29) | def warning(self, *args):
method error (line 32) | def error(self, *args):
method critical (line 35) | def critical(self, *args):
function dump_function (line 41) | def dump_function(function):
function load_function (line 47) | def load_function(function):
function scan_dirs (line 53) | def scan_dirs(path, file_callback):
function isinstance_str (line 61) | def isinstance_str(object, class_name):
function profile_function (line 70) | def profile_function(function):
class Array_Interface (line 91) | class Array_Interface():
method __init__ (line 92) | def __init__(self, pointer, typestr, shape, read_only=False):
class IBuffer (line 99) | class IBuffer():
method ctype (line 101) | def ctype(self):
method __len__ (line 104) | def __len__(self):
method buffer (line 107) | def buffer(self):
method size_in_bytes (line 110) | def size_in_bytes(self):
method as_array_interface (line 114) | def as_array_interface(self, shape=None):
method as_np_array (line 132) | def as_np_array(self, shape=None):
FILE: __init__.py
function register (line 15) | def register():
function unregister (line 18) | def unregister():
FILE: plugins/Experimental/RenderLayer/OpaqueLayer.py
class OpaqueLayer (line 4) | class OpaqueLayer(PipelineNode):
method __init__ (line 6) | def __init__(self, pipeline):
method reflect_inputs (line 10) | def reflect_inputs(cls):
method reflect_outputs (line 16) | def reflect_outputs(cls):
method execute (line 21) | def execute(self, parameters):
FILE: plugins/Experimental/__init__.py
class ExperimentalNodes (line 4) | class ExperimentalNodes(PipelinePlugin):
method poll_pipeline (line 7) | def poll_pipeline(self, pipeline):
method register_graph_libraries (line 11) | def register_graph_libraries(self, graphs):
FILE: plugins/PluginExample/__init__.py
class PluginExample (line 4) | class PluginExample(PipelinePlugin):
method poll_pipeline (line 7) | def poll_pipeline(self, pipeline):
method register_graph_libraries (line 11) | def register_graph_libraries(self, graphs):
FILE: scripts/build_intellisense_glsl.py
function add_type (line 24) | def add_type(type_name):
function add_generic_type (line 28) | def add_generic_type(type_name):
function output_function (line 42) | def output_function(signatures, docstring):
function resolve_generics (line 57) | def resolve_generics(signatures, generic_types, has_optional, docstring):
FILE: scripts/format.py
function scan_dirs (line 1) | def scan_dirs(path, file_callback):
function fix_whitespace (line 14) | def fix_whitespace(path):
FILE: scripts/print_pixel_formats.py
function print_format_prop (line 13) | def print_format_prop(format, prop):
function print_format_props (line 17) | def print_format_props(format):
FILE: scripts/setup_blender_addon.py
function build_lib (line 16) | def build_lib(path):
function ensure_dir (line 31) | def ensure_dir(path):
function make_link (line 35) | def make_link(point_from, point_to):
function make_copy (line 45) | def make_copy(copy_to, copy_from):
Condensed preview — 208 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,218K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 1600,
"preview": "name: Bug Reports\ndescription: Open a new issue\nbody:\n\n- type: markdown\n attributes:\n value: |\n Before opening "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 342,
"preview": "blank_issues_enabled: false\n\ncontact_links:\n - name: Q&A\n url: https://github.com/bnpr/Malt/discussions/categories/q"
},
{
"path": ".github/workflows/BlenderMalt.yml",
"chars": 4590,
"preview": "name: BLENDERMALT_PACKAGE_AND_RELEASE\n\non:\n push:\n\njobs:\n release:\n runs-on: ubuntu-latest\n steps:\n - uses: a"
},
{
"path": ".gitignore",
"chars": 93,
"preview": ".*\n!.gitignore\n!.gitattributes\n!.gitmodules\n\n!.github\n\n__pycache__\n\n*.lib\n*.dll\n*.so\n\n*.log\n\n"
},
{
"path": "BlenderMalt/CBlenderMalt/CBlenderMalt.cpp",
"chars": 3289,
"preview": "#include \"stdio.h\"\n#include \"mikktspace.h\"\n\n#ifdef _WIN32\n#define EXPORT extern \"C\" __declspec( dllexport )\n#else\n#defin"
},
{
"path": "BlenderMalt/CBlenderMalt/CMakeLists.txt",
"chars": 414,
"preview": "cmake_minimum_required(VERSION 3.10)\n\nSET(CMAKE_CXX_STANDARD 17)\n# set(CMAKE_GENERATOR_PLATFORM x64)\n\nproject(CBlenderMa"
},
{
"path": "BlenderMalt/CBlenderMalt/__init__.py",
"chars": 1060,
"preview": "import subprocess\nimport os\nimport ctypes\n\nimport platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\n\nlibrar"
},
{
"path": "BlenderMalt/CBlenderMalt/build.py",
"chars": 650,
"preview": "import subprocess\nimport os\nimport platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\nbuild_dir = os.path.jo"
},
{
"path": "BlenderMalt/CBlenderMalt/mikktspace.c",
"chars": 57439,
"preview": "/** \\file mikktspace/mikktspace.c\n * \\ingroup mikktspace\n */\n/**\n * Copyright (C) 2011 by Morten S. Mikkelsen\n *\n * T"
},
{
"path": "BlenderMalt/CBlenderMalt/mikktspace.h",
"chars": 8209,
"preview": "/** \\file mikktspace/mikktspace.h\n * \\ingroup mikktspace\n */\n/**\n * Copyright (C) 2011 by Morten S. Mikkelsen\n *\n * T"
},
{
"path": "BlenderMalt/MaltLights.py",
"chars": 2508,
"preview": "import math\nimport bpy\n\nclass MaltLight(bpy.types.PropertyGroup):\n\n def sync_data(self, context):\n light = sel"
},
{
"path": "BlenderMalt/MaltMaterial.py",
"chars": 9927,
"preview": "import os\nimport bpy\nfrom BlenderMalt.MaltUtils import malt_path_set_transform, malt_path_get_transform\nfrom . MaltPrope"
},
{
"path": "BlenderMalt/MaltMeshes.py",
"chars": 6618,
"preview": "import ctypes\nimport bpy\n\nMESHES = {}\n\ndef get_mesh_name(object):\n name = object.name_full\n if len(object.modifier"
},
{
"path": "BlenderMalt/MaltNodes/MaltCustomPasses.py",
"chars": 5522,
"preview": "import bpy\nfrom BlenderMalt import MaltPipeline\n\nclass MaltIOParameter(bpy.types.PropertyGroup):\n\n def get_parameter_"
},
{
"path": "BlenderMalt/MaltNodes/MaltNode.py",
"chars": 15142,
"preview": "from Malt.PipelineParameters import Parameter, Type\nimport bpy \nfrom BlenderMalt.MaltProperties import MaltPropertyGr"
},
{
"path": "BlenderMalt/MaltNodes/MaltNodeTree.py",
"chars": 27559,
"preview": "import os, time\nfrom Malt.SourceTranspiler import GLSLTranspiler, PythonTranspiler\nimport bpy\nfrom BlenderMalt.MaltPrope"
},
{
"path": "BlenderMalt/MaltNodes/MaltNodeUITools.py",
"chars": 20100,
"preview": "import bpy\r\nimport string\r\nfrom typing import Union\r\nfrom bpy.types import NodeTree\r\nfrom bpy.props import PointerProper"
},
{
"path": "BlenderMalt/MaltNodes/MaltSocket.py",
"chars": 7039,
"preview": "import bpy\n\n__TYPE_COLORS = {\n 'bool': (0.8, 0.65, 0.84, 1.0),\n 'Bool': (0.8, 0.65, 0.84, 1.0),\n 'float': (0.63"
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltArrayIndexNode.py",
"chars": 1905,
"preview": "from Malt.PipelineParameters import Parameter, Type\nimport bpy \nfrom BlenderMalt.MaltNodes.MaltNode import MaltNode\n\n"
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltFunctionNode.py",
"chars": 11064,
"preview": "from Malt.PipelineParameters import Type, Parameter, MaterialParameter, GraphParameter\nimport bpy \nfrom BlenderMalt.M"
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltFunctionSubCategory.py",
"chars": 2961,
"preview": "import bpy\nfrom BlenderMalt.MaltNodes.Nodes.MaltFunctionNode import MaltFunctionNodeBase\n\nclass MaltFunctionSubCategoryN"
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltIONode.py",
"chars": 10471,
"preview": "import bpy \nfrom BlenderMalt.MaltNodes.MaltNode import MaltNode\nfrom BlenderMalt.MaltProperties import MaltPropertyGr"
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltInlineNode.py",
"chars": 3035,
"preview": "import bpy \nfrom BlenderMalt.MaltNodes.MaltNode import MaltNode\n\n\nclass MaltInlineNode(bpy.types.Node, MaltNode):\n "
},
{
"path": "BlenderMalt/MaltNodes/Nodes/MaltStructNode.py",
"chars": 1870,
"preview": "import bpy \nfrom BlenderMalt.MaltNodes.MaltNode import MaltNode\n\n\nclass MaltStructNode(bpy.types.Node, MaltNode):\n "
},
{
"path": "BlenderMalt/MaltNodes/_init_.py",
"chars": 1080,
"preview": "def get_modules():\n from . import MaltNode, MaltNodeUITools, MaltNodeTree, MaltSocket, MaltCustomPasses\n modules ="
},
{
"path": "BlenderMalt/MaltPipeline.py",
"chars": 13573,
"preview": "import os, platform, time\nimport bpy\nfrom BlenderMalt.MaltUtils import malt_path_set_transform, malt_path_get_transform\n"
},
{
"path": "BlenderMalt/MaltProperties.py",
"chars": 42008,
"preview": "import os\nimport bpy\nfrom Malt.PipelineParameters import Type, Parameter, MaterialParameter\nfrom Malt import Scene\nfrom "
},
{
"path": "BlenderMalt/MaltRenderEngine.py",
"chars": 23779,
"preview": "import ctypes, time, platform\nimport xxhash\nimport bpy\nimport gpu\nfrom mathutils import Vector, Matrix, Quaternion\nfrom "
},
{
"path": "BlenderMalt/MaltTextures.py",
"chars": 2702,
"preview": "import ctypes\nfrom . import MaltPipeline\n\n__TEXTURES = {}\n\ndef get_texture(texture):\n name = texture.name_full\n if"
},
{
"path": "BlenderMalt/MaltUtils.py",
"chars": 4724,
"preview": "import bpy\n\nclass OT_MaltPrintError(bpy.types.Operator):\n bl_idname = \"wm.malt_print_error\"\n bl_label = \"Print Mal"
},
{
"path": "BlenderMalt/__init__.py",
"chars": 7297,
"preview": "bl_info = {\n \"name\": \"BlenderMalt\",\n \"description\" : \"Extensible Python Render Engine\",\n \"author\" : \"Miguel Poz"
},
{
"path": "BlenderMalt/readme.md",
"chars": 2051,
"preview": "# BlenderMalt\n\n## Introduction\n\n*BlenderMalt* is a [*Malt Host*](../Malt#malt-pipelines) for *Blender*. \nIt handles all"
},
{
"path": "Bridge/Client_API.py",
"chars": 11389,
"preview": "import ctypes, logging as LOG, io, sys\nfrom Bridge.ipc import SharedBuffer\n\ndef bridge_method(function):\n def result("
},
{
"path": "Bridge/Docs.py",
"chars": 6227,
"preview": "def build_docs(pipeline, docs_path):\n import os\n output_path = os.path.join(docs_path, 'reference')\n parameters"
},
{
"path": "Bridge/Material.py",
"chars": 1822,
"preview": "from Malt.PipelineParameters import Parameter\n\nMATERIAL_SHADERS = {}\n\nclass Material():\n\n def __init__(self, path, pi"
},
{
"path": "Bridge/Mesh.py",
"chars": 337,
"preview": "MESHES = {}\n\ndef load_mesh(pipeline, msg):\n name = msg['name']\n data = msg['data']\n\n MESHES[name] = pipeline.lo"
},
{
"path": "Bridge/Proxys.py",
"chars": 1524,
"preview": "from Malt.GL.Mesh import Mesh\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.Texture import Gradient\nfrom Malt.Scene i"
},
{
"path": "Bridge/Server.py",
"chars": 20647,
"preview": "import importlib\nimport os, sys, time, ctypes, os, copy\nimport cProfile, pstats, io\nimport multiprocessing.connection as"
},
{
"path": "Bridge/Texture.py",
"chars": 1120,
"preview": "from Malt.GL import Texture\nfrom Malt.GL.GL import *\n\nTEXTURES = {}\n\ndef load_texture(msg):\n name = msg['name']\n d"
},
{
"path": "Bridge/__init__.py",
"chars": 1476,
"preview": "def reload():\n import importlib\n from . import Client_API, Server, Material, Mesh, Texture\n for module in [ Cli"
},
{
"path": "Bridge/ipc/CMakeLists.txt",
"chars": 379,
"preview": "cmake_minimum_required(VERSION 3.10)\n\n# set(CMAKE_GENERATOR_PLATFORM x64)\n\nproject(Ipc)\n\nSET(CMAKE_BUILD_TYPE Release)\nS"
},
{
"path": "Bridge/ipc/__init__.py",
"chars": 4015,
"preview": "import os, ctypes, platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\n\nlibrary = 'libIpc.so'\nif platform.sys"
},
{
"path": "Bridge/ipc/build.py",
"chars": 650,
"preview": "import subprocess\nimport os\nimport platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\nbuild_dir = os.path.jo"
},
{
"path": "Bridge/ipc/ipc.c",
"chars": 676,
"preview": "#ifdef _WIN32\n#define EXPORT __declspec( dllexport )\n#else\n#define EXPORT __attribute__ ((visibility (\"default\")))\n#endi"
},
{
"path": "Bridge/ipc/ipc.h",
"chars": 10190,
"preview": "/* ipc.h - v0.2 - public domain cross platform inter process communication\n no warranty implied; use "
},
{
"path": "Bridge/renderdoc/CMakeLists.txt",
"chars": 375,
"preview": "cmake_minimum_required(VERSION 3.10)\n\n# set(CMAKE_GENERATOR_PLATFORM x64)\n\nproject(RenderDocWrapper)\n\nSET(CMAKE_BUILD_TY"
},
{
"path": "Bridge/renderdoc/__init__.py",
"chars": 489,
"preview": "import subprocess\nimport os\nimport ctypes\n\nimport platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\n\nlibrar"
},
{
"path": "Bridge/renderdoc/build.py",
"chars": 650,
"preview": "import subprocess\nimport os\nimport platform\n\nsrc_dir = os.path.abspath(os.path.dirname(__file__))\nbuild_dir = os.path.jo"
},
{
"path": "Bridge/renderdoc/renderdoc_app.h",
"chars": 28925,
"preview": "/******************************************************************************\n * The MIT License (MIT)\n *\n * Copyright"
},
{
"path": "Bridge/renderdoc/renderdoc_wrapper.c",
"chars": 1161,
"preview": "#include \"renderdoc_app.h\"\n#include <stddef.h>\n\n#ifdef _WIN32\n#define EXPORT __declspec( dllexport )\n#else\n#define EXPOR"
},
{
"path": "LICENSE",
"chars": 1103,
"preview": "MIT License\n\nMalt - Copyright (c) 2020-2022 BNPR, Miguel Pozo and contributors\n\nPermission is hereby granted, free of ch"
},
{
"path": "LICENSE - DEPENDENCIES",
"chars": 1689,
"preview": "Malt:\n\nPyOpenGL\nBSD License\nhttp://pyopengl.sourceforge.net\nhttps://github.com/mcfletch/pyopengl/blob/master/license.txt"
},
{
"path": "LICENSE - DEPENDENCIES (FULL TEXT)",
"chars": 68807,
"preview": "Malt:\n********************************************************************************\nPyOpenGL\nBSD License\nhttp://pyope"
},
{
"path": "Malt/GL/GL.py",
"chars": 2605,
"preview": "import collections\n\nimport OpenGL\n#OpenGL.ERROR_LOGGING = False\n#OpenGL.FULL_LOGGING = False\n#OpenGL.ERROR_ON_COPY = Fal"
},
{
"path": "Malt/GL/GLSLEval.py",
"chars": 1186,
"preview": "def glsl_vector(convert, length, *args):\n unpacked_args = []\n for arg in args:\n try:\n unpacked_a"
},
{
"path": "Malt/GL/GLSLParser/CMakeLists.txt",
"chars": 676,
"preview": "cmake_minimum_required(VERSION 3.15)\nproject(GLSLParser)\nset(CMAKE_CXX_STANDARD 17)\n\nadd_subdirectory(external)\n\nset(CMA"
},
{
"path": "Malt/GL/GLSLParser/build.py",
"chars": 676,
"preview": "import subprocess\nimport os\nimport platform\n\ncurrent_dir = os.path.abspath(os.path.dirname(__file__))\nbuild_dir = os.pat"
},
{
"path": "Malt/GL/GLSLParser/external/CMakeLists.txt",
"chars": 632,
"preview": "include(FetchContent)\ninclude(ExternalProject)\n\nFetchContent_Declare(\n pegtl\n GIT_REPOSITORY https://github.com/taocpp"
},
{
"path": "Malt/GL/GLSLParser/external/PEGTL-LICENSE",
"chars": 1103,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2007-2021 Dr. Colin Hirsch and Daniel Frey\n\nPermission is hereby granted, free of c"
},
{
"path": "Malt/GL/GLSLParser/external/rapidjson-LICENSE",
"chars": 5151,
"preview": "Tencent is pleased to support the open source community by making RapidJSON available. \n \nCopyright (C) 2015 THL A29 Lim"
},
{
"path": "Malt/GL/GLSLParser/src/main.cpp",
"chars": 13629,
"preview": "#include <string>\n#include <map>\n#include <iostream>\n\n#include <tao/pegtl.hpp>\n#include <tao/pegtl/contrib/analyze.hpp>\n"
},
{
"path": "Malt/GL/Mesh.py",
"chars": 3980,
"preview": "import ctypes\n\nfrom Malt.GL.GL import *\n\nclass Mesh():\n\n def __init__(self, position, index, normal=None, tangent=Non"
},
{
"path": "Malt/GL/RenderTarget.py",
"chars": 3616,
"preview": "import ctypes\n\nfrom Malt.GL.GL import *\n\n\nclass RenderTarget():\n\n def __init__(self, targets=[], depth_stencil=None):"
},
{
"path": "Malt/GL/Shader.py",
"chars": 23347,
"preview": "import ctypes, os\n\nfrom Malt.GL.GL import *\nfrom Malt.Utils import LOG\n\n\nclass Shader():\n\n def __init__(self, vertex_"
},
{
"path": "Malt/GL/Texture.py",
"chars": 9042,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL import Mesh\n\n\nclass Texture():\n\n def __init__(self, resolution, internal_format"
},
{
"path": "Malt/GL/readme.md",
"chars": 3469,
"preview": "# GL\n\nThe GL folder contains a series of modules that abstracts commonly needed OpenGL functionality. \nIt doesn't try t"
},
{
"path": "Malt/Nodes/LineRender.py",
"chars": 2752,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pip"
},
{
"path": "Malt/Nodes/SceneFilter.py",
"chars": 1982,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pip"
},
{
"path": "Malt/Nodes/SuperSamplingAA.py",
"chars": 1486,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pip"
},
{
"path": "Malt/Pipeline.py",
"chars": 17136,
"preview": "import math, os, ctypes\nfrom os import path\n\nfrom Malt.Utils import LOG\n\nfrom Malt.GL.GL import *\nfrom Malt.GL.Mesh impo"
},
{
"path": "Malt/PipelineGraph.py",
"chars": 9915,
"preview": "from Malt.Utils import scan_dirs, LOG\n\n\nclass PipelineGraphIO():\n\n def __init__(self, name, dynamic_input_types = [],"
},
{
"path": "Malt/PipelineNode.py",
"chars": 2053,
"preview": "class PipelineNode():\n\n def __init__(self, pipeline):\n self.pipeline = pipeline\n \n @staticmethod\n def"
},
{
"path": "Malt/PipelineParameters.py",
"chars": 6452,
"preview": "class PipelineParameters():\n\n def __init__(self, scene={}, world={}, camera={}, object={}, material={}, mesh={}, ligh"
},
{
"path": "Malt/PipelinePlugin.py",
"chars": 1550,
"preview": "from Malt.Utils import isinstance_str\n\nclass PipelinePlugin():\n\n @classmethod\n def poll_pipeline(self, pipeline):\n"
},
{
"path": "Malt/Pipelines/MiniPipeline/MiniPipeline.py",
"chars": 2052,
"preview": "from os import path\n\nfrom Malt.GL.GL import *\nfrom Malt.GL.Mesh import Mesh\nfrom Malt.GL.RenderTarget import RenderTarge"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/NPR_LightShaders.py",
"chars": 2501,
"preview": "\nfrom Malt.GL.GL import *\nfrom Malt.GL.Shader import UBO\nfrom Malt.GL.Texture import TextureArray\nfrom Malt.GL.RenderTar"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/NPR_Lighting.py",
"chars": 5848,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Shader import UBO\nfrom Malt.GL.Texture import TextureArray, CubeMapArray\nfrom Malt"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py",
"chars": 12643,
"preview": "from os import path\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.GL.Texture import Texture\n\nfrom Malt.Pipelin"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/Render/RenderLayers.py",
"chars": 4556,
"preview": "from Malt.GL import GL\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pipel"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py",
"chars": 6704,
"preview": "from Malt.GL.GL import *\nfrom Malt.PipelineNode import PipelineNode\nfrom Malt.PipelineParameters import Parameter, Type\n"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/Render/ScreenPass.py",
"chars": 2424,
"preview": "from Malt.GL import GL\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pipel"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/MainPass.py",
"chars": 3394,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pip"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/PrePass.py",
"chars": 4865,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pip"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/ScreenPass.py",
"chars": 3656,
"preview": "from Malt.GL import GL\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\nfrom Malt.Pipel"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Intellisense.glsl",
"chars": 372,
"preview": "//This is just for getting text editor autocompletion on user shaders.\n//It doesn't have any effect at runtime\n#ifdef __"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl",
"chars": 2129,
"preview": "#define NO_NORMAL_INPUT\n#define NO_UV_INPUT\n#define NO_VERTEX_COLOR_INPUT\n#define NO_MODEL_INPUT\n#define NO_ID_INPUT\n\n#i"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_MeshShader.glsl",
"chars": 6788,
"preview": "#include \"NPR_Intellisense.glsl\"\n#include \"Common.glsl\"\n\n/* META GLOBAL\n @meta: internal=true; \n*/\nstruct NPR_Setting"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Pipeline/NPR_Filters.glsl",
"chars": 8883,
"preview": "#ifndef NPR_FILTERS_GLSL\n#define NPR_FILTERS_GLSL\n\n#include \"Filters/AO.glsl\"\n#include \"Filters/Bevel.glsl\"\n#include \"Fi"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Pipeline/NPR_Lighting.glsl",
"chars": 4602,
"preview": "#ifndef NPR_LIGHTING_GLSL\n#define NPR_LIGHTING_GLSL\n\n#include \"Lighting/Lighting.glsl\"\n\nuniform usampler2DArray SHADOWMA"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Pipeline/NPR_Mesh.glsl",
"chars": 5275,
"preview": "#ifndef NPR_MESH_GLSL\n#define NPR_MESH_GLSL\n\n#include \"NPR_Pipeline/NPR_Filters.glsl\"\n#include \"NPR_Pipeline/NPR_Shading"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Pipeline/NPR_Shading.glsl",
"chars": 4774,
"preview": "#ifndef NPR_SHADING_GLSL\n#define NPR_SHADING_GLSL\n\n#include \"NPR_Lighting.glsl\"\n#include \"Shading/ShadingModels.glsl\"\n\n/"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Pipeline/NPR_Shading2.glsl",
"chars": 14307,
"preview": "#ifndef NPR_SHADING2_GLSL\n#define NPR_SHADING2_GLSL\n\n#include \"NPR_Lighting.glsl\"\n#include \"Shading/ShadingModels.glsl\"\n"
},
{
"path": "Malt/Pipelines/NPR_Pipeline/Shaders/NPR_ScreenShader.glsl",
"chars": 1322,
"preview": "#define NO_UV_INPUT\n#define NO_VERTEX_COLOR_INPUT\n#define NO_MODEL_INPUT\n\n#include \"NPR_Intellisense.glsl\"\n\n#ifdef PIXEL"
},
{
"path": "Malt/Render/Common.py",
"chars": 1825,
"preview": "import ctypes\n\nfrom Malt.GL.Shader import UBO\n\nclass C_CommonBuffer(ctypes.Structure):\n _fields_ = [\n ('CAMERA"
},
{
"path": "Malt/Render/DepthToCompositeDepth.py",
"chars": 1050,
"preview": "from Malt.GL.GL import *\nfrom Malt.GL.Texture import Texture\nfrom Malt.GL.RenderTarget import RenderTarget\n\n_shader_src="
},
{
"path": "Malt/Render/Lighting.py",
"chars": 13556,
"preview": "import math\nimport ctypes\n\nimport pyrr\n\nfrom Malt.GL.GL import *\nfrom Malt.GL.Shader import UBO\nfrom Malt.GL.Texture imp"
},
{
"path": "Malt/Render/Sampling.py",
"chars": 1751,
"preview": "import math\n\nimport random\n#Don't share state\nrandom = random.Random()\n\n#Rotated Grid Super Sampling pattern\n#https://en"
},
{
"path": "Malt/Render/readme.md",
"chars": 679,
"preview": "# Render Library\n\nAny *Pipeline* agnostic render utility that can't be implemented only as *GLSL* code should go here. "
},
{
"path": "Malt/Scene.py",
"chars": 1752,
"preview": "class Camera():\n\n def __init__(self, camera_matrix, projection_matrix, parameters={}):\n self.camera_matrix = c"
},
{
"path": "Malt/Shaders/Common/Color.glsl",
"chars": 3520,
"preview": "#ifndef COMMON_COLOR_GLSL\n#define COMMON_COLOR_GLSL\n\n/* META GLOBAL\n @meta: category=Color;\n*/\n\n/* META\n @meta:d"
},
{
"path": "Malt/Shaders/Common/Hash.glsl",
"chars": 793,
"preview": "#ifndef COMMON_HASH_GLSL\n#define COMMON_HASH_GLSL\n\n/* META GLOBAL\n @meta: category=Math; internal=True;\n*/\n\nuvec4 _p"
},
{
"path": "Malt/Shaders/Common/Mapping.glsl",
"chars": 2729,
"preview": "#ifndef COMMON_MAPPING_GLSL\n#define COMMON_MAPPING_GLSL\n\n#include \"Common.glsl\"\n\n/* META GLOBAL\n @meta: category=Vect"
},
{
"path": "Malt/Shaders/Common/Math.glsl",
"chars": 3740,
"preview": "#ifndef COMMON_MATH_GLSL\n#define COMMON_MATH_GLSL\n\n/* META GLOBAL\n @meta: category=Math; internal=true;\n*/\n\n//C Stan"
},
{
"path": "Malt/Shaders/Common/Matrix.glsl",
"chars": 1364,
"preview": "#ifndef COMMON_MATRIX_GLSL\n#define COMMON_MATRIX_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Matrix;\n*/\n\n"
},
{
"path": "Malt/Shaders/Common/Normal.glsl",
"chars": 5041,
"preview": "#ifndef COMMON_NORMAL_GLSL\n#define COMMON_NORMAL_GLSL\n\n#include \"Common.glsl\"\n\n/* META GLOBAL\n @meta: category=Vecto"
},
{
"path": "Malt/Shaders/Common/Quaternion.glsl",
"chars": 1676,
"preview": "#ifndef COMMON_QUATERNION_GLSL\n#define COMMON_QUATERNION_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Qua"
},
{
"path": "Malt/Shaders/Common/Transform.glsl",
"chars": 5741,
"preview": "#ifndef COMMON_TRANSFORM_GLSL\n#define COMMON_TRANSFORM_GLSL\n\n/* META GLOBAL\n @meta: category=Vector; internal=true;\n"
},
{
"path": "Malt/Shaders/Common.glsl",
"chars": 3536,
"preview": "#ifndef COMMON_GLSL\n#define COMMON_GLSL\n\n#ifdef VERTEX_SHADER\n#define vertex_out out\n#else\n#define vertex_out in\n#endif\n"
},
{
"path": "Malt/Shaders/Filters/AO.glsl",
"chars": 2029,
"preview": "#ifndef AO_GLSL\n#define AO_GLSL\n\n#include \"Common/Math.glsl\"\n#include \"Common/Transform.glsl\"\n\n/* META\n @meta: inter"
},
{
"path": "Malt/Shaders/Filters/Bevel.glsl",
"chars": 2925,
"preview": "#ifndef FILTERS_BEVEL_GLSL\n#define FILTERS_BEVEL_GLSL\n\n#include \"Common/Math.glsl\"\n#include \"Common/Transform.glsl\"\n\n/* "
},
{
"path": "Malt/Shaders/Filters/Blur.glsl",
"chars": 6608,
"preview": "#ifndef BLUR_GLSL\n#define BLUR_GLSL\n\n/* META GLOBAL\n @meta: category=Filter; subcategory=Blur;\n*/\n\n/* META\n @inp"
},
{
"path": "Malt/Shaders/Filters/Curvature.glsl",
"chars": 1842,
"preview": "#ifndef CURVATURE_GLSL\n#define CURVATURE_GLSL\n\n#include \"Common/Math.glsl\"\n\n/* META GLOBAL\n @meta: category=Filter;\n"
},
{
"path": "Malt/Shaders/Filters/JumpFlood.glsl",
"chars": 1594,
"preview": "#ifndef JUMP_FLOOD_GLSL\n#define JUMP_FLOOD_GLSL\n\n/* META\n @width: default=1.0;\n*/\n//input should be a texture where "
},
{
"path": "Malt/Shaders/Filters/Kuwahara.glsl",
"chars": 3768,
"preview": "#ifndef KUWAHARA_GLSL\r\n#define KUWAHARA_GLSL\r\n\r\n/* META GLOBAL\r\n @meta: category=Filter; subcategory=Kuwahara;\r\n*/\r\n\r"
},
{
"path": "Malt/Shaders/Filters/Line.glsl",
"chars": 10642,
"preview": "#ifndef LINE_GLSL\n#define LINE_GLSL\n\n#define LINE_DEPTH_MODE_NEAR 0\n#define LINE_DEPTH_MODE_FAR 1\n#define LINE_DEPTH_MO"
},
{
"path": "Malt/Shaders/Filters/Sharpen.glsl",
"chars": 1799,
"preview": "#ifndef SHARPEN_GLSL\r\n#define SHARPEN_GLSL\r\n\r\n#include \"Filters/Blur.glsl\"\r\n\r\n/* META GLOBAL\r\n @meta: category=Filte"
},
{
"path": "Malt/Shaders/Filters/StructureTensor.glsl",
"chars": 1415,
"preview": "#ifndef STRUCTURE_TENSOR_GLSL\r\n#define STRUCTURE_TENSOR_GLSL\r\n\r\n// Described in: https://en.wikipedia.org/wiki/Structure"
},
{
"path": "Malt/Shaders/Intellisense/intellisense.glsl",
"chars": 52096,
"preview": "\n//This file contains a series of C++ macros, structs and function declarations\n//to make GLSL autocompletion work with "
},
{
"path": "Malt/Shaders/Lighting/Lighting.glsl",
"chars": 5904,
"preview": "#ifndef COMMON_LIGHTING_GLSL\n#define COMMON_LIGHTING_GLSL\n\n#include \"Common.glsl\"\n\n#ifndef MAX_LIGHTS\n #define MAX_LI"
},
{
"path": "Malt/Shaders/Node Utils/bool.glsl",
"chars": 445,
"preview": "#ifndef BOOL_GLSL\n#define BOOL_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nbool bool_and(bool a, bool b) { ret"
},
{
"path": "Malt/Shaders/Node Utils/common.glsl",
"chars": 1013,
"preview": "#ifndef NODE_UTILS_COMMON_GLSL\n#define NODE_UTILS_COMMON_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nvec3 surf"
},
{
"path": "Malt/Shaders/Node Utils/float.glsl",
"chars": 1841,
"preview": "#ifndef FLOAT_GLSL\n#define FLOAT_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nfloat float_add(float a, float b)"
},
{
"path": "Malt/Shaders/Node Utils/int.glsl",
"chars": 969,
"preview": "#ifndef INT_GLSL\n#define INT_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nint int_add(int a, int b){ return a+b"
},
{
"path": "Malt/Shaders/Node Utils/node_utils.glsl",
"chars": 752,
"preview": "#ifndef NODE_UTILS_GLSL\n#define NODE_UTILS_GLSL\n\n#include \"Node Utils/common.glsl\"\n#include \"Node Utils/bool.glsl\"\n#incl"
},
{
"path": "Malt/Shaders/Node Utils/packing.glsl",
"chars": 507,
"preview": "#ifndef PACKING_GLSL\n#define PACKING_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nuvec4 pack_8bit(vec4 a, vec4 "
},
{
"path": "Malt/Shaders/Node Utils/properties.glsl",
"chars": 530,
"preview": "#ifndef PROPERTIES_GLSL\n#define PROPERTIES_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nbool bool_property(bool"
},
{
"path": "Malt/Shaders/Node Utils/sampler.glsl",
"chars": 717,
"preview": "#ifndef SAMPLER_GLSL\n#define SAMPLER_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nvec4 sampler1D_sample(sampler"
},
{
"path": "Malt/Shaders/Node Utils/vec2.glsl",
"chars": 1627,
"preview": "#ifndef VEC2_GLSL\n#define VEC2_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\nvec2 vec2_add(vec2 a, vec2 b){ retu"
},
{
"path": "Malt/Shaders/Node Utils/vec3.glsl",
"chars": 2934,
"preview": "#ifndef VEC3_GLSL\n#define VEC3_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\n/*META @a: subtype=Vector; @b: subt"
},
{
"path": "Malt/Shaders/Node Utils/vec4.glsl",
"chars": 2854,
"preview": "#ifndef VEC4_GLSL\n#define VEC4_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*/\n\n/*META @a: subtype=Vector; @b: subt"
},
{
"path": "Malt/Shaders/Node Utils 2/Bool.glsl",
"chars": 699,
"preview": "#ifndef NODE_UTILS_2_BOOL_GLSL\n#define NODE_UTILS_2_BOOL_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Boo"
},
{
"path": "Malt/Shaders/Node Utils 2/Color.glsl",
"chars": 873,
"preview": "#ifndef NODE_UTILS_2_COLOR_GLSL\n#define NODE_UTILS_2_COLOR_GLSL\n\n/* META GLOBAL\n @meta: category=Color;\n*/\n\n/* META"
},
{
"path": "Malt/Shaders/Node Utils 2/ColorBlend.glsl",
"chars": 11041,
"preview": "#ifndef COMMON_BLEND_MODES_GLSL\n#define COMMON_BLEND_MODES_GLSL\n\n#include \"Common/Color.glsl\"\n\n/* META GLOBAL\n @meta"
},
{
"path": "Malt/Shaders/Node Utils 2/Filter.glsl",
"chars": 148,
"preview": "#ifndef NODE_UTILS_2_FILTER_GLSL\n#define NODE_UTILS_2_FILTER_GLSL\n\n/* META GLOBAL\n @meta: category=Filter;\n*/\n\n#endif"
},
{
"path": "Malt/Shaders/Node Utils 2/Float.glsl",
"chars": 3716,
"preview": "#ifndef NODE_UTILS_2_FLOAT_GLSL\n#define NODE_UTILS_2_FLOAT_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=F"
},
{
"path": "Malt/Shaders/Node Utils 2/Input.glsl",
"chars": 6494,
"preview": "#ifndef NODE_UTILS_2_INPUT_GLSL\n#define NODE_UTILS_2_INPUT_GLSL\n\n#include \"Shading/BRDF.glsl\"\n\n/* META GLOBAL\n @meta"
},
{
"path": "Malt/Shaders/Node Utils 2/Int.glsl",
"chars": 1595,
"preview": "#ifndef NODE_UTILS_2_INT_GLSL\n#define NODE_UTILS_2_INT_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Integ"
},
{
"path": "Malt/Shaders/Node Utils 2/Mat4.glsl",
"chars": 439,
"preview": "#ifndef NODE_UTILS_2_MAT4_GLSL\n#define NODE_UTILS_2_MAT4_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Mat"
},
{
"path": "Malt/Shaders/Node Utils 2/Parameters.glsl",
"chars": 1167,
"preview": "#ifndef NODE_UTILS_2_PARAMETERS_GLSL\n#define NODE_UTILS_2_PARAMETERS_GLSL\n\n/*META GLOBAL\n @meta: category=Parameters;"
},
{
"path": "Malt/Shaders/Node Utils 2/Texturing.glsl",
"chars": 6498,
"preview": "#ifndef NODE_UTILS_2_TEXTURING_GLSL\n#define NODE_UTILS_2_TEXTURING_GLSL\n\n#include \"Procedural/Fractal_Noise.glsl\"\n#inclu"
},
{
"path": "Malt/Shaders/Node Utils 2/Vec2.glsl",
"chars": 3847,
"preview": "#ifndef NODE_UTILS_2_VEC2_GLSL\n#define NODE_UTILS_2_VEC2_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Vec"
},
{
"path": "Malt/Shaders/Node Utils 2/Vec3.glsl",
"chars": 6018,
"preview": "#ifndef NODE_UTILS_2_VEC3_GLSL\n#define NODE_UTILS_2_VEC3_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Vec"
},
{
"path": "Malt/Shaders/Node Utils 2/Vec4.glsl",
"chars": 5067,
"preview": "#ifndef NODE_UTILS_2_VEC4_GLSL\n#define NODE_UTILS_2_VEC4_GLSL\n\n/* META GLOBAL\n @meta: category=Math; subcategory=Vec"
},
{
"path": "Malt/Shaders/Node Utils 2/Vector.glsl",
"chars": 2817,
"preview": "#ifndef NODE_UTILS_2_VECTOR_GLSL\n#define NODE_UTILS_2_VECTOR_GLSL\n\n/* META GLOBAL\n @meta: category=Vector;\n*/\n\n/* M"
},
{
"path": "Malt/Shaders/Node Utils 2/conversion.glsl",
"chars": 11345,
"preview": "#ifndef NODE_UTILS_2_CONVERSION_GLSL\n#define NODE_UTILS_2_CONVERSION_GLSL\n\n/* META GLOBAL\n @meta: internal = true;\n*"
},
{
"path": "Malt/Shaders/Node Utils 2/node_utils_2.glsl",
"chars": 1027,
"preview": "#ifndef NODE_UTILS_2_GLSL\n#define NODE_UTILS_2_GLSL\n\n#include \"Node Utils 2/conversion.glsl\"\n#include \"Node Utils 2/Bool"
},
{
"path": "Malt/Shaders/Passes/BlendTexture.glsl",
"chars": 365,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/BlendTransparency.glsl",
"chars": 1443,
"preview": "#include \"Common.glsl\"\n#include \"Common/Color.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER"
},
{
"path": "Malt/Shaders/Passes/CopyTextures.glsl",
"chars": 950,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/DepthToBlenderDepth.glsl",
"chars": 440,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/JumpFlood.glsl",
"chars": 413,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/LineComposite.glsl",
"chars": 948,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/Unpack8bitTextures.glsl",
"chars": 607,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Passes/sRGBConversion.glsl",
"chars": 593,
"preview": "#include \"Common.glsl\"\n\n#ifdef VERTEX_SHADER\nvoid main()\n{\n DEFAULT_SCREEN_VERTEX_SHADER();\n}\n#endif\n\n#ifdef PIXEL_SH"
},
{
"path": "Malt/Shaders/Procedural/Bayer.glsl",
"chars": 1181,
"preview": "#ifndef BAYER_GLSL\n#define BAYER_GLSL\n\n// Based on: https://en.wikipedia.org/wiki/Ordered_dithering\n\n/* META GLOBAL\n "
},
{
"path": "Malt/Shaders/Procedural/Cell_Noise.glsl",
"chars": 4289,
"preview": "#ifndef PROCEDURAL_CELL_NOISE_GLSL\n#define PROCEDURAL_CELL_NOISE_GLSL\n\n#include \"Common/Hash.glsl\"\n\n/* META GLOBAL\n "
},
{
"path": "Malt/Shaders/Procedural/Cell_Noise.inl",
"chars": 1590,
"preview": "//Should define T, DIMENSIONS and TILE\n//Should declare coord and tile_size (if TILE)\nT real_coord = coord;\n#if TILE != "
},
{
"path": "Malt/Shaders/Procedural/Fractal_Noise.glsl",
"chars": 3286,
"preview": "#ifndef PROCEDURAL_FRACTAL_NOISE_GLSL\n#define PROCEDURAL_FRACTAL_NOISE_GLSL\n\n#include \"Noise.glsl\"\n\n/* META GLOBAL\n "
},
{
"path": "Malt/Shaders/Procedural/Fractal_Noise.inl",
"chars": 659,
"preview": "//Should define TILE\n//Should declare coord and tile_size (if TILE)\n\nvec4 color = vec4(0);\nfloat weight = 1.0;\nfloat tot"
},
{
"path": "Malt/Shaders/Procedural/Noise.glsl",
"chars": 2482,
"preview": "#ifndef PROCEDURAL_NOISE_GLSL\n#define PROCEDURAL_NOISE_GLSL\n\n#include \"Common/Hash.glsl\"\n\n/* META GLOBAL\n @meta: int"
},
{
"path": "Malt/Shaders/Procedural/Noise.inl",
"chars": 2303,
"preview": "//Should define T, DIMENSIONS and TILE\n//Should declare coord and tile_size (if TILE)\n#if TILE != 0\n T _tile_size = r"
},
{
"path": "Malt/Shaders/SDF/SDF.glsl",
"chars": 3539,
"preview": "#ifndef SDF_SDF_GLSL\n#define SDF_SDF_GLSL\n\n#include \"Common/Transform.glsl\"\n\n// SDF functions adapted from https://www.i"
},
{
"path": "Malt/Shaders/Shading/BRDF.glsl",
"chars": 4728,
"preview": "#ifndef BRDF_GLSL\n#define BRDF_GLSL\n\n// The following formulas follow the naming conventions explained in the LitSurface"
},
{
"path": "Malt/Shaders/Shading/ShadingModels.glsl",
"chars": 4285,
"preview": "#ifndef SHADING_MODELS_GLSL\n#define SHADING_MODELS_GLSL\n\n#include \"Shading/BRDF.glsl\"\n\n/* META GLOBAL\n @meta: catego"
},
{
"path": "Malt/Shaders/readme.md",
"chars": 1612,
"preview": "# Shader Library\n\nAll *GLSL* code should go here. \nAll shader files have their own [*include guard*](https://en.wikiped"
},
{
"path": "Malt/SourceTranspiler.py",
"chars": 6946,
"preview": "import textwrap\n\n#TODO: Send transpiler along graph types\nclass SourceTranspiler():\n \n @classmethod\n def get_so"
},
{
"path": "Malt/Utils.py",
"chars": 3556,
"preview": "import logging\n\nclass MaltLogger():\n\n def __init__(self):\n self.last_msg = None\n self.repeated_msg = 0\n"
},
{
"path": "Malt/__init__.py",
"chars": 1,
"preview": "\n"
},
{
"path": "Malt/readme.md",
"chars": 5180,
"preview": "# Malt\n\n## Introduction\n\n*Malt* is a fully customizable rendering framework written in **Python** and **OpenGL**.\n\nIts m"
},
{
"path": "README.md",
"chars": 2690,
"preview": "# Malt\n\nMalt is a fully customizable real-time rendering framework for animation and illustration. \nIt's aimed at advan"
},
{
"path": "__init__.py",
"chars": 653,
"preview": "# Some people are used to download the zipped repo from Github to install Blender addons.\n# This file is just to redirec"
},
{
"path": "docs/Documentation/Getting Started.md",
"chars": 1944,
"preview": "# Gettting Started\n\n## Requirements\n\n- OpenGL 4.5 support.\n- Latest Blender stable release.\n\n> A dedicated Nvidia or AMD"
},
{
"path": "docs/Documentation/Graphs.md",
"chars": 9808,
"preview": "# Graphs & Nodes\n\n*Pipelines* and *Plugins* declare their own *Node Tree* subtypes, a.k.a *Graph* types.\n\nNodes have the"
},
{
"path": "docs/Documentation/Plugins.md",
"chars": 666,
"preview": "# Plugins\n\nRender pipelines can be customized through plugins.\nPlugins can:\n\n- Add new node libraries to *Pipeline Graph"
},
{
"path": "docs/Documentation/Settings.md",
"chars": 4225,
"preview": "# Setup & Settings\n\n## Addon Settings\n\n\n\n- **Open Session Log** \n>Opens the current session"
},
{
"path": "docs/Documentation/Tooling.md",
"chars": 1273,
"preview": "# External Tools\n\n## VSCode\n\n*Malt* has built-in integration with [VSCode](https://code.visualstudio.com), including a w"
},
{
"path": "docs/FAQ.md",
"chars": 1087,
"preview": "# FAQ\n\n# What's the difference between Malt and BEER?\n\n| | Malt | BEER |\n|---|------|------|\n| **Target Audience** | A"
},
{
"path": "docs/From-Nodes-To-Code/From-Nodes-To-Code.md",
"chars": 11316,
"preview": "# From Nodes To Code\n\nThis tutorial will teach you how to make your own *Malt* shaders.\n\n*Malt* shaders are written in *"
},
{
"path": "docs/Setup-BlenderMalt-for-Development.md",
"chars": 898,
"preview": "# How to Setup BlenderMalt for Development\n\n- Clone the repository. \n`git clone https://github.com/bnpr/Malt.git`\n- Set"
},
{
"path": "docs/extra/extra.css",
"chars": 621,
"preview": "/* https://github.com/squidfunk/mkdocs-material/issues/1635#issuecomment-811058692 */\n\n[data-md-color-scheme=\"slate\"] {\n"
},
{
"path": "docs/extra/extra.js",
"chars": 250,
"preview": "/* https://github.com/squidfunk/mkdocs-material/issues/1635#issuecomment-811058692 */\n\ndocument.querySelectorAll('.zoom'"
},
{
"path": "docs/index.md",
"chars": 2697,
"preview": "---\nhide:\n #- toc\n #- navigation\n---\n\n# Malt\n\n\n***Malt*** is a fully ***customizable real-time rendering*** framework "
},
{
"path": "docs/overrides/partials/_footer.html",
"chars": 0,
"preview": ""
},
{
"path": "docs/reference/Light-graph.md",
"chars": 47038,
"preview": "# Light Graph Reference\n---\n## Input\n---\n### **Geometry**\n- **Inputs** \n\t- **Space** *: ( int | ENUM(Object,World,Camer"
},
{
"path": "docs/reference/Mesh-graph.md",
"chars": 53861,
"preview": "# Mesh Graph Reference\n---\n## Input\n---\n### **Pass Info**\n- **Outputs** \n\t- **Is Main Pass** *: ( bool )* \n\t- **Is Pre"
},
{
"path": "docs/reference/Render Layer-graph.md",
"chars": 2243,
"preview": "# Render Layer Graph Reference\n---\n## Render\n---\n### **LineRender**\nExpands the line up to the width especified in the *"
},
{
"path": "docs/reference/Render-graph.md",
"chars": 2771,
"preview": "# Render Graph Reference\n---\n## Render\n---\n### **LineRender**\nExpands the line up to the width especified in the *Line W"
},
{
"path": "docs/reference/Screen-graph.md",
"chars": 52520,
"preview": "# Screen Graph Reference\n---\n## Input\n---\n### **Geometry**\n- **Inputs** \n\t- **Space** *: ( int | ENUM(Object,World,Came"
},
{
"path": "docs/reference/settings.md",
"chars": 2310,
"preview": "# Malt Settings\n## World\n- **Material**\n\t- **Default** *: ( Material )** = Malt - Default Mesh Material* \n\t>The default"
},
{
"path": "mkdocs/mkdocs.yml",
"chars": 2248,
"preview": "site_name: Malt\n\nsite_url: https://malt3d.com\nsite_author: Miguel Pozo\nsite_description: Malt Render Engine\nrepo_name: b"
},
{
"path": "mkdocs/netlify.toml",
"chars": 127,
"preview": "[build]\n publish = \"site/\"\n command = \"mkdocs build\"\n ignore = \"git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../"
},
{
"path": "mkdocs/requirements.txt",
"chars": 23,
"preview": "mkdocs-material==8.2.8\n"
},
{
"path": "plugins/Experimental/RenderLayer/OpaqueLayer.py",
"chars": 819,
"preview": "from Malt.PipelineNode import PipelineNode\nfrom Malt.PipelineParameters import Parameter, Type\n\nclass OpaqueLayer(Pipeli"
},
{
"path": "plugins/Experimental/__init__.py",
"chars": 445,
"preview": "import os\nfrom Malt.PipelinePlugin import PipelinePlugin, isinstance_str\n\nclass ExperimentalNodes(PipelinePlugin):\n\n "
},
{
"path": "plugins/PluginExample/Shaders/PluginExample.glsl",
"chars": 109,
"preview": "#include \"Common.glsl\"\n#include \"Common/Hash.glsl\"\n\nvec4 instance_random_color()\n{\n return hash(ID[0]);\n}\n"
},
{
"path": "plugins/PluginExample/__init__.py",
"chars": 519,
"preview": "import os\nfrom Malt.PipelinePlugin import PipelinePlugin, isinstance_str\n\nclass PluginExample(PipelinePlugin):\n\n @cla"
},
{
"path": "scripts/build_intellisense_glsl.py",
"chars": 6401,
"preview": "import os, json, xmltodict, traceback, copy\n\nSKIP_TERMS = ['image','atomic','barrier','memory','shadow','noise']\n\nDEFINE"
},
{
"path": "scripts/format.py",
"chars": 1590,
"preview": "def scan_dirs(path, file_callback):\n import os\n for e in os.scandir(path):\n if e.is_file():\n ext"
},
{
"path": "scripts/generate_conversion_nodes.py",
"chars": 2183,
"preview": "result = ''\n\nbase_types = ('float','int','uint','bool')\nvector_types = ('vec','ivec','uvec','bvec')\n\nfor to_base_type in"
},
{
"path": "scripts/generate_license_dependencies_full.py",
"chars": 713,
"preview": "import os\n\nTOP_DIR = os.path.join(os.path.dirname(__file__), os.pardir)\n\ndependencies = open(os.path.join(TOP_DIR, 'LICE"
}
]
// ... and 8 more files (download for full content)
About this extraction
This page contains the full source code of the BlenderNPR/BEER GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 208 files (1.1 MB), approximately 319.6k tokens, and a symbol index with 1014 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.