@@ -56,7 +56,7 @@ function _getBuiltinGlobalsCache(strandsContext) {
5656function getBuiltinGlobalNode ( strandsContext , name ) {
5757 const spec = BUILTIN_GLOBAL_SPECS [ name ]
5858 if ( ! spec ) return null
59-
59+
6060 const cache = _getBuiltinGlobalsCache ( strandsContext )
6161 const uniformName = `_p5_global_${ name } `
6262 const cached = cache . nodes . get ( uniformName )
@@ -154,7 +154,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
154154 }
155155
156156 // Convert value to a StrandsNode if it isn't already
157- const valueNode = value instanceof StrandsNode ? value : p5 . strandsNode ( value ) ;
157+ const valueNode = value ?. isStrandsNode ? value : p5 . strandsNode ( value ) ;
158158
159159 // Create a new CFG block for the early return
160160 const earlyReturnBlockID = CFG . createBasicBlock ( cfg , BlockType . DEFAULT ) ;
@@ -369,12 +369,17 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
369369 fn [ typeInfo . fnName ] = function ( ...args ) {
370370 if ( strandsContext . active ) {
371371 if ( args . length === 1 && args [ 0 ] . dimension && args [ 0 ] . dimension === typeInfo . dimension ) {
372- const { id, dimension } = build . functionCallNode ( strandsContext , typeInfo . fnName , args , {
373- overloads : [ {
374- params : [ args [ 0 ] . typeInfo ( ) ] ,
375- returnType : typeInfo ,
376- } ]
377- } ) ;
372+ const { id, dimension } = build . functionCallNode (
373+ strandsContext ,
374+ strandsContext . backend . getTypeName ( typeInfo . baseType , typeInfo . dimension ) ,
375+ args ,
376+ {
377+ overloads : [ {
378+ params : [ args [ 0 ] . typeInfo ( ) ] ,
379+ returnType : typeInfo ,
380+ } ]
381+ }
382+ ) ;
378383 return createStrandsNode ( id , dimension , strandsContext ) ;
379384 } else {
380385 // For vector types with a single argument, repeat it for each component
@@ -431,7 +436,7 @@ function createHookArguments(strandsContext, parameters){
431436 const oldDependsOn = dag . dependsOn [ structNode . id ] ;
432437 const newDependsOn = [ ...oldDependsOn ] ;
433438 let newValueID ;
434- if ( val instanceof StrandsNode ) {
439+ if ( val ?. isStrandsNode ) {
435440 newValueID = val . id ;
436441 }
437442 else {
@@ -463,7 +468,7 @@ function createHookArguments(strandsContext, parameters){
463468 return args ;
464469}
465470function enforceReturnTypeMatch ( strandsContext , expectedType , returned , hookName ) {
466- if ( ! ( returned instanceof StrandsNode ) ) {
471+ if ( ! ( returned ?. isStrandsNode ) ) {
467472 // try {
468473 const result = build . primitiveConstructorNode ( strandsContext , expectedType , returned ) ;
469474 return result . id ;
@@ -485,7 +490,12 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
485490 }
486491 if ( receivedType . dimension !== expectedType . dimension ) {
487492 if ( receivedType . dimension !== 1 ) {
488- FES . userError ( 'type error' , `You have returned a vector with ${ receivedType . dimension } components in ${ hookName } when a ${ expectedType . baseType + expectedType . dimension } was expected!` ) ;
493+ const receivedTypeDisplay = receivedType . baseType + ( receivedType . dimension > 1 ? receivedType . dimension : '' ) ;
494+ const expectedTypeDisplay = expectedType . baseType + expectedType . dimension ;
495+ FES . userError ( 'type error' ,
496+ `You have returned a ${ receivedTypeDisplay } in ${ hookName } when a ${ expectedTypeDisplay } was expected!\n\n` +
497+ `Make sure your hook returns the correct type.`
498+ ) ;
489499 }
490500 else {
491501 const result = build . primitiveConstructorNode ( strandsContext , expectedType , returned ) ;
@@ -573,10 +583,27 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) {
573583 const handleRetVal = ( retNode ) => {
574584 if ( isStructType ( expectedReturnType ) ) {
575585 const expectedStructType = structType ( expectedReturnType ) ;
576- if ( retNode instanceof StrandsNode ) {
586+ if ( retNode ?. isStrandsNode ) {
577587 const returnedNode = getNodeDataFromID ( strandsContext . dag , retNode . id ) ;
578588 if ( returnedNode . baseType !== expectedStructType . typeName ) {
579- FES . userError ( "type error" , `You have returned a ${ retNode . baseType } from ${ hookType . name } when a ${ expectedStructType . typeName } was expected.` ) ;
589+ const receivedTypeName = returnedNode . baseType || 'undefined' ;
590+ const receivedDim = dag . dimensions [ retNode . id ] ;
591+ const receivedTypeDisplay = receivedDim > 1 ?
592+ `${ receivedTypeName } ${ receivedDim } ` : receivedTypeName ;
593+
594+ const expectedProps = expectedStructType . properties
595+ . map ( p => p . name ) . join ( ', ' ) ;
596+ FES . userError ( 'type error' ,
597+ `You have returned a ${ receivedTypeDisplay } from ${ hookType . name } when a ${ expectedStructType . typeName } was expected.\n\n` +
598+ `The ${ expectedStructType . typeName } struct has these properties: { ${ expectedProps } }\n\n` +
599+ `Instead of returning a different type, you should modify and return the ${ expectedStructType . typeName } struct that was passed to your hook.\n\n` +
600+ `For example:\n` +
601+ `${ hookType . name } ((inputs) => {\n` +
602+ ` // Modify properties of inputs\n` +
603+ ` inputs.someProperty = ...;\n` +
604+ ` return inputs; // Return the modified struct\n` +
605+ `})`
606+ ) ;
580607 }
581608 const newDeps = returnedNode . dependsOn . slice ( ) ;
582609 for ( let i = 0 ; i < expectedStructType . properties . length ; i ++ ) {
@@ -595,10 +622,14 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) {
595622 const propName = expectedProp . name ;
596623 const receivedValue = retNode [ propName ] ;
597624 if ( receivedValue === undefined ) {
598- FES . userError ( 'type error' , `You've returned an incomplete struct from ${ hookType . name } .\n` +
599- `Expected: { ${ expectedReturnType . properties . map ( p => p . name ) . join ( ', ' ) } }\n` +
600- `Received: { ${ Object . keys ( retNode ) . join ( ', ' ) } }\n` +
601- `All of the properties are required!` ) ;
625+ const expectedProps = expectedReturnType . properties . map ( p => p . name ) . join ( ', ' ) ;
626+ const receivedProps = Object . keys ( retNode ) . join ( ', ' ) ;
627+ FES . userError ( 'type error' ,
628+ `You've returned an incomplete ${ expectedStructType . typeName } struct from ${ hookType . name } .\n\n` +
629+ `Expected properties: { ${ expectedProps } }\n` +
630+ `Received properties: { ${ receivedProps } }\n\n` +
631+ `All properties are required! Make sure to include all properties in the returned struct.`
632+ ) ;
602633 }
603634 const expectedTypeInfo = expectedProp . dataType ;
604635 const returnedPropID = enforceReturnTypeMatch ( strandsContext , expectedTypeInfo , receivedValue , hookType . name ) ;
0 commit comments