mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-06 15:48:37 +08:00
202 lines
6.0 KiB
JavaScript
202 lines
6.0 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Pass the data of a pass under a technique and organize it into a tree structure
|
|
* @param passData
|
|
*/
|
|
exports.buildEffect = function(index, passData) {
|
|
|
|
const props = passData.props;
|
|
const defs = passData.defines;
|
|
|
|
const tree = {
|
|
name: `Pass ${index}${passData.phase ? ' - ' + passData.phase : ''}`,
|
|
type: 'cc.Object',
|
|
childMap: {},
|
|
};
|
|
|
|
const hideAttrs = ['USE_INSTANCING'];
|
|
|
|
function encode(item) {
|
|
let current = tree;
|
|
|
|
/**
|
|
* USE_INSTANCING is common to every child in passes
|
|
* To make editing easier, they are referred to the outside of the passes
|
|
* At this point, you need to set each of the passes to be non-editable and invisible
|
|
*/
|
|
|
|
if (hideAttrs.includes(item.name)) {
|
|
item.visible = false;
|
|
}
|
|
|
|
if (item.defines && item.defines.length) {
|
|
item.defines.forEach((name) => {
|
|
|
|
// The defines starting with ! are reverse dependencies, the data position will not change
|
|
if (name.startsWith('!')) {
|
|
return;
|
|
}
|
|
|
|
let child = current.childMap[name];
|
|
if (!child) {
|
|
child = current.childMap[name] = {
|
|
name,
|
|
// type: 'ui.Depend',
|
|
type: 'Boolean',
|
|
childMap: {},
|
|
};
|
|
}
|
|
current = child;
|
|
});
|
|
}
|
|
|
|
if (current.childMap[item.name]) {
|
|
const tempChildMap = current.childMap[item.name].childMap;
|
|
current.childMap[item.name] = item;
|
|
item.childMap = tempChildMap;
|
|
} else {
|
|
current.childMap[item.name] = item;
|
|
item.childMap = {};
|
|
}
|
|
current.childMap[item.name].name = item.name;
|
|
}
|
|
|
|
defs.forEach((item) => {
|
|
switch (item.type) {
|
|
case 'Number':
|
|
item.type = 'Enum';
|
|
item.enumList = [];
|
|
for (let i = item.range[0]; i <= item.range[1]; i++) {
|
|
item.enumList.push({
|
|
name: i,
|
|
value: i,
|
|
});
|
|
}
|
|
break;
|
|
case 'String':
|
|
item.type = 'Enum';
|
|
item.enumList = item.options.map((str) => {
|
|
return {
|
|
name: str,
|
|
value: str,
|
|
};
|
|
});
|
|
break;
|
|
case 'Enum': break; // Fix the problem that item.type === 'Enum' is reset to 'ui.Depend'
|
|
default:
|
|
// item.type = 'ui.Depend';
|
|
item.type = 'Boolean';
|
|
}
|
|
encode(item);
|
|
});
|
|
props.forEach(encode);
|
|
|
|
function encodeStates(item) {
|
|
let current = tree;
|
|
if (current.childMap[item.name]) {
|
|
const tempChildMap = current.childMap[item.name].childMap;
|
|
current.childMap[item.name] = item;
|
|
item.childMap = tempChildMap;
|
|
} else {
|
|
current.childMap[item.name] = item;
|
|
item.childMap = {};
|
|
}
|
|
|
|
if (item.isObject && item.value) {
|
|
current = current.childMap[item.name];
|
|
Object.keys(item.value).forEach((name) => {
|
|
let child = current.childMap[name];
|
|
if (!child) {
|
|
child = current.childMap[name] = {
|
|
name,
|
|
type: 'cc.Object',
|
|
childMap: {},
|
|
};
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function modifyType(item) {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (item.isObject) {
|
|
Object.keys(item.value).forEach((key) => {
|
|
modifyType(item.value[key]);
|
|
});
|
|
} else if (item.isArray) {
|
|
item.value.forEach((data) => {
|
|
modifyType(data);
|
|
});
|
|
|
|
modifyType(item.elementTypeData);
|
|
} else {
|
|
switch (item.type) {
|
|
case 'Number':
|
|
if (item.isEnum) {
|
|
item.type = 'Enum';
|
|
item.enumList = [];
|
|
Object.keys(item.enumData).forEach((key) => {
|
|
item.enumList.push({
|
|
name: key,
|
|
value: item.enumData[key],
|
|
});
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
modifyType(passData.states);
|
|
encodeStates(passData.states);
|
|
|
|
function translate(item) {
|
|
const children = Object.keys(item.childMap).map((name) => {
|
|
const child = item.childMap[name];
|
|
translate(child);
|
|
return child;
|
|
});
|
|
if (item) {
|
|
item.children = children;
|
|
}
|
|
return item;
|
|
}
|
|
const dump = translate(tree);
|
|
dump.value = tree.childMap;
|
|
return dump;
|
|
};
|
|
|
|
exports.materialTechniquePolyfill = function(origin) {
|
|
let useInstancing;
|
|
const passes = origin.passes.map((data, index) => {
|
|
// Merge data.defines and data.props
|
|
const pass = exports.buildEffect(index, data);
|
|
pass.switch = data.switch;
|
|
pass.propertyIndex = data.propertyIndex;
|
|
|
|
if (!useInstancing && pass.childMap.USE_INSTANCING) {
|
|
useInstancing = JSON.parse(JSON.stringify(pass.childMap.USE_INSTANCING));
|
|
useInstancing.visible = true;
|
|
}
|
|
return pass;
|
|
});
|
|
|
|
/**
|
|
* USE_INSTANCING is common to every child in passes
|
|
* For ease of editing, they are referred to outside of the passes
|
|
* Two external variables useInstancing, useBatching are provided to dock
|
|
* The value of the first pass takes precedence
|
|
*/
|
|
const technique = {
|
|
name: origin.name,
|
|
passes,
|
|
useInstancing,
|
|
};
|
|
|
|
return technique;
|
|
};
|